Entity framework 实体框架为嵌套集合生成的查询

Entity framework 实体框架为嵌套集合生成的查询,entity-framework,hibernate,jpa,nhibernate,orm,Entity Framework,Hibernate,Jpa,Nhibernate,Orm,使用实体框架6 假设我有一个实体Parent,它有两个嵌套集合ICollection和ICollection。我急切地想把这两样东西都拿来: dbContext.Parent.Include(p=>p.Child).Include(p=>Child2.ToList() 这将生成一个大查询,在较高级别上如下所示: SELECT ... FROM ( SELECT (parent columns), (child columns), NULL as (child2 columns) FR

使用实体框架6

假设我有一个实体
Parent
,它有两个嵌套集合
ICollection
ICollection
。我急切地想把这两样东西都拿来:

dbContext.Parent.Include(p=>p.Child).Include(p=>Child2.ToList()

这将生成一个大查询,在较高级别上如下所示:

SELECT ... FROM (
   SELECT (parent columns), (child columns), NULL as (child2 columns)
   FROM Parent left join Child on ... 
   WHERE (filter on Parent)
   UNION ALL
   SELECT (parent columns), NULL as (child columns), (child2 columns)
   FROM Parent left join Child2 on ...  
   WHERE (filter on Parent)
))
在NHibernate(或JPA、EclipseLink、Hibernate等)中,有没有一种方法可以使实体框架的行为类似于批取,在这种方法中,您可以指定首先查询父表,然后分别查询每个子表

 SELECT ... from Parent -- as usual
 SELECT ... from Child where parent_id in (list of IDs)
 SELECT ... from Child2 where parent_id in (list of IDs)
 -- alternatively, you can specify EXISTS instead of IN LIST:
 SELECT ... from Child where exists (select 1 from Parent where child.parent_id = parent.id  and (where clause for parent))

我发现这更容易理解和解释,因为它更类似于手工编写的SQL。此外,它还可以防止结果集中出现冗余的父表行。另一方面,它有更多的往返过程。

我认为实体框架不可能做到这一点,至少使用LINQ是不可能的。在一天结束时,ORM尝试生成可能的最有效的查询,至少对它是这样。也就是说,类似于实体的ORMs并不总是生成外观最好或效率最高的SQL。我的猜测,这只是一个猜测,是实体正试图减少行程和I/O的数量,因为在相对论中,I/O是经验

如果您正在寻找对SQL的细粒度控制,我建议您避免使用ORM,或者像我一样,使用实体进行基本CRUD和简单查询,使用存储过程进行复杂查询,例如复杂报表。ADO.NET也一直存在,但您似乎更倾向于使用ORM


你也可以认为这很有用。基本上没有太多的调整可用

实体框架错过了NHibernate提供的许多复杂功能。EF的独特卖点是它的多功能LINQ支持,但如果您需要声明式控制ORM如何跨多个表获取数据,EF不是首选工具。使用EF,您只能尝试寻找程序技巧

让我通过展示如何使用EF实现“批量抓取”来说明这一点:

这会将所需的数据加载到上下文中,EF通过关系修复连接父级和子级。结果是一个
家长
列表,其中填充了他们的两个子集合。但当然,它有几个缺点:

  • 您需要禁用延迟加载,因为即使子集合已填充,它们也不会标记为已加载。启用时,访问它们仍会触发延迟加载
  • 重复代码:您需要将谓词重复三次。要避免这一点并不容易
  • 太具体了。对于每个不同的场景,即使它们几乎相同,也必须编写一组新的语句。或者使其可配置,这仍然是一个程序解决方案
  • EF当前的主要生产版本(6)没有查询批处理功能。您需要像EntityFramework.Extended这样的第三方工具,以便在一次数据库往返中运行这些查询
context.Configuration.LazyLoadingEnabled = false;
context.Children1.Where(c1 => parentIds.Contains(c1.ParentId)).Load();
context.Children2.Where(c2 => parentIds.Contains(c2.ParentId)).Load();
var parents = dbContext.Parent.Where(p => parentIds.Contains(p.Id)).ToList();