EF永远需要生成此查询

时间:2010-08-17 15:48:27

标签: linq performance entity-framework

我有父子表关系。在存储库中,我这样做:

return (from p in _ctx.Parents  
.Include( "Children" )  
select p).AsQueryable<Parent>();  

然后在过滤器中,我想通过子ID列表过滤父项:

IQueryable<Parent> qry;  // from above
List<int> ids;  // huge list (8500)
var filtered =
from p in qry.Where( p => p.Children.Any(c => ids.Contains(c.ChildId)) ) select s;  

我的ID列表非常庞大。这会生成一个简单的SQL语句,它确实有一个很大的id列表“in(1,2,3 ...)”,但它本身不需要花费很多时间。然而,EF只需要整整一分钟来生成语句。我通过设置断点并调用:

来证明这一点
((ObjectQuery<Parent>)filtered).ToTraceString();

这需要所有时间。这是我上一次linq声明中的问题吗?我不知道在(ids)中执行相当于Child.ChildId的任何其他方法。即使我的linq陈述不好,这个世界应该花多长时间?

2 个答案:

答案 0 :(得分:4)

不幸的是,在Linq to Entities中构建查询是一个非常重要的问题,但我发现它通常可以节省时间,因为能够在实际访问数据库之前从其组件构建查询。

他们实现Contains方法的方式可能是使用一种算法,该算法假定Contains通常用于相对较小的数据集。根据我的测试,列表中每个ID所花费的时间开始在8000左右飙升。

因此,将查询分解为多个部分可能会有所帮助。将它们分组为1000或更少的组,并连接一堆Where表达式。

var idGroups = ids.GroupBy(i => i / 1000);
var q = Parents.Include("Children").AsQueryable();
var newQ = idGroups.Aggregate(q, 
    (s, g) => s.Concat(
                  q.Where(w => w.Children.Any(wi => g.Contains(wi.ChildId)))));

这会显着加快速度,但对于您的目的而言可能不够明显,在这种情况下,您将不得不求助于存储过程。不幸的是,这个特定的用例并不适合预期的实体框架行为的“框”。如果您的ID列表可以作为来自同一实体上下文的查询开始,则实体框架可以正常工作。

答案 1 :(得分:2)

使用Lambda语法重写您的查询,它会将时间缩短3秒(或至少为我的EF项目做的时间)。

return _ctx.Parents.Include( "Children" ).AsQueryable<Parent>();  

IQueryable<Parent> qry;  // from above
List<int> ids;  // huge list (8500)
var filtered = qry.Where( p => p.Children.Any(c => ids.Contains(c.ChildId)) );