我们有几个具有多个 1:1 关系的类用于快速连接,虽然这对于用于表格显示的匿名类型来说效果很好,但我不确定如何在单个 linq 查询中完全填充该类型。
我们拥有这些属性是因为它是 1:1 关闭,或者我们不想通过子集合查询以找到每个显示器的“主要”,我们通过在保存时设置这些主要 ID 来承担成本。
本文上下文的精简示例:
public class Contact
{
public long Id { get; set; }
public EntitySet<Address> Addresses { get; set; }
public EntityRef<Address> PrimaryAddress { get; set; }
public long? PrimaryAddressId { get; set; }
public EntitySet<Email> Emails { get; set; }
public EntityRef<Email> PrimaryEmail { get; set; }
public long? PrimaryEmailId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Address
{
public long Id { get; set; }
public EntitySet<Contact> Contacts { get; set; }
public bool IsPrimary { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
public class Email
{
public long Id { get; set; }
public EntitySet<Contact> Contacts { get; set; }
public bool IsPrimary { get; set; }
public string Address { get; set; }
}
问题是在显示联系人列表时,PrimaryAddress
和 PrimaryEmail
必须延迟加载。如果我们执行 DataLoadOptions
,它也不会产生预期的效果,因为它是 1:1,例如:
var DB = new DataContext();
var dlo = new DataLoadOptions();
dlo.LoadWith<Contact>(c => c.PrimaryAddress);
dlo.LoadWith<Contact>(c => c.PrimaryEmail);
DB.LoadOptions = dlo;
var result = from c in DB.Contacts select c;
result.ToList();
上面的代码产生了一个INNER JOIN,因为它把它当作父关系,它不考虑可为空的 FK 关系并左连接 1:1 属性。所需的查询类似于:
Select t1.*, t.2*, t3.*
From Contact t1
Left Join Address t2 On t1.PrimayAddressId = t2.Id
Left Join Email On t1.PrimaryEmailId = t3.Id
有没有一种方法可以做到这一点并获得一个填充了这些可空的 1:1 属性的 IQueryable,甚至是一个列表?由于其他限制,我们需要类型为 Contact
,因此匿名类型将不起作用。对选项非常开放,任何东西都比延迟加载 n*(number of 1:1s)+1 查询我们显示的行数要好。
最佳答案
更新: 终于开始更新这个了,devart 的人已经修复了更高版本中的行为以完美地工作。根本不需要 DataLoadOptions
,只需使用表格外的字段即可,例如:
var DB = new DataContext();
var result = from c in DB.Contacts
select new {
c.Id
c.FirstName,
c.LastName,
Address = c.PrimaryAddress.Street1 + " " + c.PrimaryAddress.Street2 //...
Email = c.PrimaryEmail.Address
};
这正确地对相关的 Address
和 Email
表执行单个左外连接。现在修复是特定于获取此匿名类型的情况...但他们还修复了我们 确实 需要它的 DataLoadOptions
行为,正确关闭外键现在输入。希望此更新对旧版本的其他人有所帮助...我强烈建议升级,自 5.35 以来的版本中有许多新的增强功能(许多让生活更更容易)。
原文:
我们最终得到的是一种不同的方法。这可能是 devart: dotConnect for Oracle 的特定行为 提供商(从 5.35.62 版开始,如果此行为发生变化,我会尝试更新此问题)。
var DB = new DataContext();
var result = from c in DB.Contacts
select new {
c.Id
c.FirstName,
c.LastName,
Address = new AddressLite {
Street1 = c.PrimaryAddress.Street1,
Street2 = c.PrimaryAddress.Street2,
City = c.PrimaryAddress.City,
State = go.PrimaryAddress.State,
Country = go.PrimaryAddress.Country },
Email = c.PrimaryEmail.Address
};
result.ToList();
这会导致单个查询。在选择中调用子对象时,例如c.PrimaryAddress
不会 导致发生连接(导致大量select ... from address where id = n
延迟加载,我们正在显示的每行表格数据一个),但是在其上调用一个属性,例如c.PrimaryAddress.Street1
DOES 在查询查询的地址表中导致正确的左连接。上面的 linq 仅适用于 linq-to-sql,它会因 linq-to-entities 上的空引用而失败,但是......在我们处理的情况下这很好。
优点:
- 单个查询,生成地址和电子邮件的左连接
- 用于地址的轻量级对象和用于电子邮件的字符串(它们在实际项目中都有一些反向引用 EntiySet,这使得它们比简单的表格显示需求所必需的更昂贵)
- 快速/干净,上面的查询比手动连接我们正在做的每个子表要简单得多,代码更简洁。
- 性能,较重对象的创建非常成功,从电子邮件更改为字符串,地址更改为 AddressLite 以及(在整个项目中)电话更改为 PhoneLite 导致仅显示表格数据的页面从 300-500 毫秒缩短到 50 -100 毫秒。
坏处:
- 匿名类型,在某些情况下我们需要一个强类型,必须创建这些类型(即使 ReSharper 完成此任务的速度一样快)会增加很多困惑。
- 由于我们无法修改和保存匿名类型,或者我们在没有大量注释工作的情况下创建的任何类型,如果模型对此有任何更改,则必须更新这些注释工作。 (因为没有生成这些类)
关于c# - Linq-to-SQL 加载 1 :1 Relations in a single query,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1753309/