c# - Entity Framework 中导航属性延迟加载的逻辑

标签 c# entity-framework lazy-loading eager-loading

我无法理解延迟加载的工作原理。例如,在下面的示例中,我可以在 Where() 子句中访问 StudentsCourses:

context.Students
       .Where(st=>st.Courses
                    .Select(c=>c.CourseName).Contains('Math')
             ).ToList();

但是,尽管我没有禁用延迟加载,但如果我不使用 Include(),下面的代码将不起作用并且会抛出 null 异常:

context.Students.Single(s => s.StudentId == 1)
       .Courses.ToList()

谁能解释一下为什么会这样?

最佳答案

为了延迟加载工作,必须发生两件事:

  1. 必须在上下文中启用延迟加载
  2. 延迟加载的属性必须是虚拟的。

在你的例子中,你已经解释过你的属性不是虚拟的,所以它不能延迟加载。但是,仅当您希望在从数据库加载基础对象后访问子实体或集合时才需要延迟加载。这意味着您在示例中做了两件截然不同的事情。

在您的第一个示例中,您编写了一个 EF 查询,其中包括对 IQueryable.Where 的调用,然后是 IQueryable.ToList。当此代码运行时,EF 将尝试将 Where 调用转换为对基础 SQL 数据存储的调用。 该调用中,您访问被查询对象的子实体引用,因此 EF 表达式解析器看到此引用并知道将其也转换为 SQL。本质上,您只要求 EF 进行一次数据库调用,因此它可以正常工作。

(一个警告:即使您的第一个查询有效,当对 ToList 的调用完成时,子集合也不会填充;SQL 查询仍然只返回所需的字段填充顶级对象。所发生的一切是 EF 在 WHERE 子句中包含子表以过滤结果集。如果您尝试访问 Courses 属性任何返回的 Student 对象,它仍然会失败。)

在您的第二个示例中,您正在调用 IQueryable.Single 以获取单个学生,然后调用 Courses 属性 getter ,然后调用调用 IQueryable.ToList。同样,EF 表达式解析器会查看 Single 方法调用中的任何内容,并将其转换为 SQL 查询,但您访问子集合的尝试发生在该调用外部。在这里,您要求 EF 执行两个“查询”:一个是获取学生,另一个是获取类(class)。由于未启用延迟加载,因此第二个查询永远不会运行,并且 EF 会立即返回 null。这会导致尝试在 null 对象上调用 ToList,这会产生预期的错误。

如果您在第二个查询中使用了 Include,EF 将被迫生成一个不同的 SQL 查询来满足您对 Single 的调用,该查询包含所有填充 Courses 子集合所需的信息。在这种情况下,当您在下一步中尝试访问 Courses 时,它不会是 null,它已经被填充,并且 ToList 调用会起作用。

要真正理解差异,最简单的方法就是查看每种情况下生成的 SQL 查询;有多种方法可以做到这一点,这里描述了一种简单的方法:

How do I view the SQL generated by the Entity Framework?

关于c# - Entity Framework 中导航属性延迟加载的逻辑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29337327/

相关文章:

C# 表达式 - 从另一个表达式创建一个表达式

c# - ConfigurationManager 没有按预期保存值

c# - C# null 条件运算符在调用异步任务时有什么区别吗?

hibernate - 当要求加载惰性字段时,Hibernate 会加载所有惰性字段

c# - 如何从另一个对象初始化一个对象

.net - ADO.NET Entity Framework : Update Wizard will not add tables

c# - 使用 Entity Framework 代码优先保存图像

c# - 具有多个表的 Linq

angular - 如何解决我网站中的性能问题

c# - c# 延迟加载中的自动属性是什么?