[英]Linq-to-SQL Load 1:1 Relations in a single query
我们有几个具有多个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
有没有办法做到这一点,并获得一个IQueryable与这些可以为空的1:1属性填充,甚至列表? 由于其他约束,我们需要类型为Contact
,因此匿名类型将不起作用。 对选项非常开放,对于我们显示的行数,任何事情都会比延迟加载n *(1:1s的数量)+1查询更好。
更新:最后解决了这个问题,开发人员已经修复了以后版本中的行为以完美地工作。 根本不需要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
确实会导致正确的左侧在地址表中查询查询加盟。 上面的linq只能在linq-to-sql中运行,它会在linq-to-entities上使用null引用失败,但是......如果我们正在处理这个问题就好了。
好处:
坏:
我们遇到了与DataLoadOptions
,延迟加载和主要记录相同的问题。
说实话,我对我们提出的解决方案并不完全满意,因为它不是很整洁,它产生的SQL查询可能很复杂,但实质上我们创建了包含我们想要强制加载的字段副本的包装类。使用子查询加载记录。 对于上面的例子:
public class ContactWithPrimary
{
public Contact Contact { get; set; }
public Email PrimaryEmail { get; set; }
public Address PrimaryAddress { get; set; }
}
然后一个示例LINQ查询将是:
List<ContactWithPrimary> Contacts = DataContext.Contacts
.Select(con => new ContactWithPrimary
{
Contact = con,
PrimaryEmail = con.PrimaryEmail,
PrimaryAddress = con.PrimaryAddress
}).ToList();
然而它的作用是在单个查询中将其拉出来。
如果在EntityRef类型属性的关联属性中将IsForeignKey设置为false,则会生成左连接。
您可能想看一下Rob Conery的Lazy List实现。
http://blog.wekeroad.com/blog/lazy-loading-with-the-lazylist/
它基本上隐藏了你的整个延迟加载实现,你不需要指定任何加载选项。
唯一的缺点是它只适用于列表。 但是,也可以为属性编写实现。 这是我的努力。
public class LazyProperty<TEntityType> where TEntityType : class
{
private readonly IQueryable<TEntityType> source;
private bool loaded;
private TEntityType entity;
public LazyProperty()
{
loaded = true;
}
public LazyProperty(IQueryable<TEntityType> source)
{
this.source = source;
}
public TEntityType Entity
{
get
{
if (!loaded)
{
entity = source.SingleOrDefault();
loaded = true;
}
return entity;
}
set
{
entity = value;
loaded = true;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.