0

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

4

1 回答 1

2

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

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

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

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

(旁注:您可以很容易地在方法语法中重现您的第二个查询,因为无论如何编译器都会通过使用该Join方法最终执行此操作,但其语法可能会变得非常复杂;我通常使用查询语法对于比单个方法调用更复杂的任何事情。)

于 2013-09-07T00:00:35.153 回答