c# – 在实体框架中使用导航属性进行延迟加载的逻辑
作者:互联网
我无法理解延迟加载的工作原理.例如,在以下示例中,我可以访问Where()子句中的学生课程:
context.Students
.Where(st=>st.Courses
.Select(c=>c.CourseName).Contains('Math')
).ToList();
但是,如果我不使用Include(),以下将无法正常工作并抛出null异常,尽管我没有禁用Lazy Loading:
context.Students.Single(s => s.StudentId == 1)
.Courses.ToList()
有人可以解释一下为什么它这样工作吗?
解决方法:
为了使延迟加载工作,必须做两件事:
>必须在上下文中启用延迟加载
>要延迟加载的属性必须是虚拟的.
在您的情况下,您已经解释过您的属性不是虚拟的,因此无法延迟加载.但是,只有在从数据库加载基础对象后才要访问子实体或集合时,才需要延迟加载.这意味着你在示例中做了两件完全不同的事情.
在第一个示例中,您编写了一个EF查询,其中包括对IQueryable.Where的调用,后跟IQueryable.ToList.当此代码运行时,EF将尝试将Where调用转换为对底层SQL数据存储的调用.在该调用中,您访问正在查询的对象的子实体引用,因此EF表达式解析器会看到此引用并知道将其转换为SQL.从本质上讲,您只是要求EF进行一次数据库调用,因此它可以工作.
(需要注意的是:即使您的第一个查询有效,但在对ToList的调用完成时也不会填充子集合; SQL查询仍然只返回填充顶级对象所需的字段.所有发生的事情是EF包含子表在WHERE子句中过滤结果集.如果您尝试访问任何返回的Student对象上的Courses属性,它仍然会失败.)
在第二个示例中,您将调用IQueryable.Single以获取单个学生,然后调用Courses属性getter,然后调用IQueryable.ToList.同样,EF表达式解析器会看到Single方法调用中的任何内容并将其转换为SQL查询,但是您在该调用之外发生了访问子集合的尝试.在这里,你要求EF做两个“查询”:一个是获得学生,另一个是获得课程.由于未启用延迟加载,因此第二个查询永远不会运行,而EF会立即返回null.这导致尝试在空对象上调用ToList,这会给出预期的错误.
如果您在第二个查询中使用了Include,则EF将被强制生成不同的SQL查询以满足您对Single的调用,其中包括填充Courses子集合所需的所有信息.在这种情况下,当您尝试在下一步中访问课程时,它将不会为空,它已经被填充,并且ToList调用将起作用.
要真正理解差异,最简单的方法就是查看每种情况下生成的SQL查询;有很多方法可以做到这一点,这里有一个简单的方法:
How do I view the SQL generated by the Entity Framework?
标签:c,entity-framework,lazy-loading,eager-loading 来源: https://codeday.me/bug/20190623/1274953.html