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

时间:2013-09-06 23:47:11

标签: c# linq entity-framework

我正在使用实体框架并构建一个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 个答案:

答案 0 :(得分:2)

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

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

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

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

(旁注:你可以很容易地在方法语法中重现你的第二个查询,因为这是编译器最终要做的事情,通过使用Join方法,但是语法可以变得非常复杂;我通常使用查询语法来处理比单个方法调用更复杂的事情。)