代码之家  ›  专栏  ›  技术社区  ›  David Spence

如何在单个linq查询中有条件地添加where子句和筛选子项?

  •  0
  • David Spence  · 技术社区  · 12 年前

    我正在使用实体框架并构建一个linq查询,以便在数据库中执行查询,以最大限度地减少返回的数据,并且查询可以有一些可选的搜索条件和一些每次都要执行的排序。我和父母和孩子一起工作(妈妈和爸爸类型)。我试图实现的过滤器是针对儿童年龄的。

    所以如果我有这样的数据。。。

    parent 1
    - child[0].Age = 5
    - child[1].Age = 10
    
    parent 2
    - child[0].Age = 7
    - child[1].Age = 23
    

    …并且我指定最小年龄为8岁,我想要显示的结果是。。。

    parent 1
    - child[1].Age = 10
    
    parent 2
    - child[1].Age = 23
    

    …如果我指定最低年龄为15岁,我打算显示。。。

    parent 2
    - child[1].Age = 23
    

    我可以用这个可怕的查询重新创建我的预期结果(我认为它实际上正在执行多个查询):

    var parents = context.Parents;
    
    if(minimumChildAge.HasValue)
    {
        parents = parents.Where(parent => parent.Children.Any(child => child.Age >= minimumChildAge.Value));
        foreach(var parent in parents)
        {
            parent.Children = parent.Children.Where(child => child.minimumChildAge.Value >= mimumumChildAge);
        }
    }
    
    parents = parents.OrderBy(x => x.ParentId).Take(50);
    

    所以我尝试了另一种方法。。。

    var query = from parent in context.Parents
                select parent;
    
    if (minimumChildAge.HasValue)
        query = from parent in query
                join child in context.Children
                on parent.ParentId equals child.ParentId
                where child.Age >= minimumChildAge.Value
                select parent;
    
    query = query.OrderBy(x => x.ParentId).Take(50);
    

    当我在linqpad中运行这个时,生成的查询看起来不错。所以我的问题。。。

    这样做正确吗?有更好的方法吗?如果我现在指定了一个最大年龄,我会写同样的联接,并希望实体框架能解决这个问题,这似乎有点有趣。此外,这对延迟加载有何影响?我希望只返回符合条件的孩子。所以当我这样做的时候 parent.Children 实体框架知道它只是查询了这些以及它在过滤集合上的工作吗?

    1 回复  |  直到 12 年前
        1
  •  2
  •   Michael Edenfield    12 年前

    假设您的上下文由实体框架数据库或类似数据库支持,那么是的,您的第一个选项是执行多个SQL查询。当您开始执行 foreach 它将运行一个SQL查询来获取父级(因为您已经强制对查询进行了枚举)。然后,对于每次尝试填充 Children 属性,它将进行另一个数据库调用。

    第二个表单应该只生成一个SQL查询;它将有大量冗余数据,但它将使用 JOIN 语句,以便在单个SQL调用中带回所有父数据和子数据,然后根据需要枚举并在客户端填充数据。

    我倾向于遵循的一条经验法则是,如果查询中的嵌套表少于4个,请尝试一次运行所有嵌套表。SQL和实体框架的查询解析器在生成该级别的联接时似乎都非常高效。

    如果超出这一点,EF可以生成的SQL查询可能会变得混乱,并且当您在单个查询上有5个以上的联接时,SQL本身(假设MSSQL)的效率会降低。没有硬性限制,因为这取决于许多特定因素,但如果我发现自己需要非常深入的嵌套,我倾向于将其分解为较小的LINQ查询,并在客户端重新组合它们。

    (附带说明:您可以很容易地用方法语法重现第二个查询,因为这就是编译器最终要做的事情,通过使用 Join 方法,但其语法可能会变得非常复杂;对于比单个方法调用更复杂的内容,我通常使用查询语法。)