2016-02-13 133 views
0

查询使用实体框架6.实体框架生成嵌套集合

假设我有两个嵌套集合ICollection<Child>ICollection<Child2>实体Parent。我想预先抓取两种:

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。此外,它还可以防止结果集中多余的父表行。另一方面,这是更多的往返旅行。

回答

0

我不认为这是可能的实体框架,至少使用LINQ。在一天结束时,ORM会尝试生成最有效的查询,至少可以。这就是说像实体这样的ORM并不总是生成最好看的SQL或最有效的。我的猜测,这只是一个猜测,实体正试图减少旅行次数和I/O因为I/O是经验,相对性。

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

你可能会觉得这很有用。基本上没有太多的调整可用。 https://stackoverflow.com/a/22390400/2272004

0

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

让我说明通过展示你需要做的,以实现“批量抓取”与EF什么:

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(); 

这将加载所需的数据进入到环境和EF连接通过家长和孩子关系修复。结果是一个parents列表与他们的两个子集合填充。但当然,它有几个缺点:

  • 您需要禁用延迟加载,因为即使子集合被填充,它们也不会被标记为加载。访问它们仍然会在启用时触发延迟加载。
  • 重复性代码:您需要重复谓词三次。避免这种情况并不容易。
  • 太具体。对于每种不同的情况,即使它们几乎相同,也必须编写一组新的语句。或者使其可配置,这仍然是一个程序化解决方案。
  • EF的当前主生产版本(6)没有查询批处理功能。您需要第三方工具,如EntityFramework.Extended,以在一次数据库往返中运行这些查询。