如何在单个 linq 查询中有条件地添加 where 子句并过滤子项

本文关键字:子句 where 添加 过滤 有条件 单个 linq 查询 | 更新日期: 2023-09-27 18:30:38

我正在使用实体框架并构建一个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实体框架是否知道它只是查询了这些内容并在过滤的集合上工作?

如何在单个 linq 查询中有条件地添加 where 子句并过滤子项

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

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

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

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

(旁注:您可以轻松地在方法语法中重现第二个查询,因为无论如何,编译器都会使用 Join 方法最终执行此操作,但语法可能会变得非常复杂;我通常使用查询语法来处理比单个方法调用更复杂的任何内容。