迁移到 EF6 后,我希望能够利用预先加载来迭代实体的相关记录的虚拟集合,以确保根据需要获得最佳查询效率。但是,当尝试在虚拟集合上使用
Include
急切加载功能时,这不起作用。
由于访问这些虚拟集合会导致数据库查询延迟加载,因此我认为会有一些控制这些查询的能力。
考虑以下定义:
class Group
{
public virtual ICollection<Member> Members {get;set;}
}
class Member
{
public int ID {get;set;}
public virtual User User {get;set;}
}
class User
{
public int ID {get;set;}
...
}
我需要查询组的所有成员以及每个关联的用户,而不是懒惰且低效地分别查询每个用户。我尝试在允许并检索数据的虚拟
Include
上使用 ICollection
,但仍然使用延迟加载而不是急切加载。
var members = group.Members.Include(m => m.User);
我可以想象,使用
ICollection
实现这些相关实体可能是出于良好的抽象原因。因此,它们似乎不是公开查询功能的明显位置。但事实是,使用这些虚拟属性是与数据库记录的相关对象交互的主要方式。由于这会导致查询,开发人员应该能够在需要时优化它们。
Include
目前仅对 IQueryable
对象起作用。在 AsQueryable
上调用 ICollection
也不起作用。实现我需要的唯一方法是从头开始构建查询。
using (var db = Context())
{
var memberIds = group.Members.Select(m => m.ID);
db.Members.Include(m => m.User).Where(m => memberIds.Includes(m.ID));
}
有谁知道在优化急切加载的同时使用虚拟道具和集合的更简单方法?我想
ICollection
上的自定义扩展应该可以按照我想要的方式使用它。
无法将实体的虚拟集合与 Include 功能一起使用
经过一番讨论才弄清楚你的意思。
鉴于...
group.Members.Include(m => m.User);
...
group
是一个 Group
对象,之前从数据库中提取出其 Members
并由 Include
急切加载,您期望上面的语句可用于加载每个成员的 User
财产。
当然,那太好了,但是...
在文档中说
您可以使用
方法指定查询结果中包含的相关数据。Include
也许它不够突出,但“在查询结果中”意味着
Include
适用于查询,即IQueryable
。您发现这一点是因为上面的语句无法编译。它在使用 group.Members.AsQueryable
时编译,但随后 Include
什么也不做,因为 AsQueryable
创建了一个没有 Include
实现的对象。解释所有这些需要太多的时间,但足以说明 group
只是一个普通的 C# 对象,不知道它是由什么创建的。它与 EF 的查询提供程序没有联系,该查询提供程序可以生成用于加载嵌套对象的查询,并且 AsQueryable
不会“注入”此查询提供程序。 (即便如此,也很难有效地做到这一点)。
假设可以。那么它就不会依赖于属性是否
virtual
。就像常规的 Inlude
方法加载虚拟和非虚拟属性一样。
它不会像你期望的那样工作。您似乎希望
group.Members.Include(m => m.User)
当场加载所有 User
对象。但 EF 的 Include
方法不会 execute 预加载,它只是 species 它。它通过指令修改 IQueryable
以立即加载嵌套数据。但 IQueryable
本身不执行任何操作,仅在强制执行时返回数据(称为 延迟执行)。
所以你要么应该
Include
从一开始就成为用户,比如......
db.Groups.Include(g => g.Members).ThenInclude(m => m.User)
...或者让延迟加载完成其工作,但正如您所说,这是低效的,因为它为每个
User
对象运行一个查询。
是的,如果能够有效地将对象图进一步扩展一层的功能就太好了。我只是不明白 EF 如何能够如此轻松地实现,或者根本不明白。
当你有两个模型并且模型之间存在关系时。像这样:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Member
{
public int Id { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
如果您加载成员而不包含成员。用户为空
如果您使用包含具有此条件的用户
User.Id == Member.UserId
加载并在member.User中设置
var member = dbContext.Members.Include(m => m.User).First();
当您使用 include 时,EF 会生成 sql 查询:
select top 1 m.*, u.*
from Members m
join users u on u.Id = m.UserId