代码之家  ›  专栏  ›  技术社区  ›  dudeNumber4

ef将永远生成此查询

  •  10
  • dudeNumber4  · 技术社区  · 14 年前

    我有亲子关系。在存储库中,我正在执行以下操作:

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

    然后在过滤器中,我想通过子ID列表来过滤父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;  
    

    我的身份证清单很大。这将生成一个简单的sql语句,其中包含大量的id“in(1,2,3…)”,但是不需要花太多时间就可以自己运行。然而,ef仅仅生成语句就需要整整一分钟。我通过设置断点并调用:

    ((ObjectQuery<Parent>)filtered).ToTraceString();
    

    这需要很多时间。我上次的林肯声明有问题吗?我不知道还有什么其他方法可以做(ids)中child.childid的等价物。即使我的linq声明不好,这到底需要多长时间?

    2 回复  |  直到 14 年前
        1
  •  4
  •   StriplingWarrior    14 年前

    不幸的是,在linq中构建对实体的查询是一个相当大的打击,但是我发现它通常可以节省时间,因为它能够在实际命中数据库之前从组件部分构建查询。

    它们实现contains方法的方式很可能使用了一种算法,该算法假定contains通常用于相对较小的数据集。根据我的测试,名单上每个身份证花费的时间开始猛增,达到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列表可以从同一个实体上下文的查询开始,那么实体框架就可以正常工作。

        2
  •  2
  •   Nate Zaugg    14 年前

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