如何获得EF查询来编译最优化的SQL?

时间:2016-07-28 09:23:51

标签: c# entity-framework linq

我对EF来说还是个新手,这一直困扰着我几天:

我有一个用户实体。它有一个父WorkSpace,它有一组Users。 每个用户还在User.Schedules属性中有一组子计划。

我在这样的对象中导航:

var query = myUser.WorkSpace.Users.SelectMany(u => u.Schedules); 

当枚举query的结果时(myUser是之前使用.Find(userid)加载的User实例),我注意到EF为WorkSpace中的每个用户向数据库发出一个查询。

为什么EF不能从单个查询中获取结果,从myUser的主键开始,并将其与所涉及的所有表连接起来?

如果我直接从这样的上下文中做其他事情,它可以正常工作:

context.Users.Where(u => u.ID = userid).SelectMany(u => u.WorkSpace.Users.SelectMany(u => u.Schedules))

我做错了吗?

1 个答案:

答案 0 :(得分:2)

我们来看第一个问题:

var query = myUser.WorkSpace.Users.SelectMany(u => u.Schedules);

如果您查看query变量的类型,您会看到它是IEnumerable<Schedule>,这意味着这是一个常规的L​​INQ to Objects查询。为什么?因为它从物化对象开始,然后加入另一个对象/集合等。这与EF延迟加载功能相结合,导致了多个数据库查询行为。

如果您对第二个查询执行相同的操作:

var query = context.Users.Where(u => u.ID = userid)
    .SelectMany(u => u.WorkSpace.Users.SelectMany(u => u.Schedules))

您会注意到query的类型现在是IQueryable<Schedule>,这意味着您现在有了LINQ to Entities查询。这是因为查询中使用的context.Users和其他对象/集合都不是真实对象 - 它们只是用于构建,执行和实现查询的元数据。

总结一下,你没有做错事。延迟加载以这种方式工作。如果您不关心所谓的N+1 query问题,可以使用第一种方法。如果你关心,那就用第二个。