繁体   English   中英

实体框架中具有导航属性的延迟加载的逻辑

[英]the logic of lazy loading with navigation properties in entity framework

我在理解延迟加载的工作方式时遇到了麻烦。 例如,在以下示例中,我可以在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的调用完成后也不会填充 ToList ; SQL查询仍然只返回填充顶级对象所需的字段。所有发生的事情是EF包括了子表(在WHERE子句中)以过滤结果集。如果您尝试访问任何返回的Student对象的Courses属性,它仍然会失败。)

在第二个示例中,您要致电IQueryable.Single以获得一个学生,然后致电Courses属性getter,再致电IQueryable.ToList 同样,EF表达式解析器会看到Single方法调用中的内容,并将其转换为SQL查询,但是您尝试访问子集合的尝试是该调用之外进行的。 在这里,您要EF进行两项“查询”:一项是让学生获得,另一项是获得课程。 由于未启用延迟加载,因此第二个查询将永远不会运行,而EF会立即返回null 这将导致尝试在null对象上调用ToList ,这将给您带来预期的错误。

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

要真正理解它们之间的区别,最简单的方法就是查看每种情况下生成的SQL查询。 有很多方法可以做到这一点,这里描述一个简单的方法:

如何查看由实体框架生成的SQL?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM