简体   繁体   English

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

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

I am having trouble understanding how lazy loading works. 我在理解延迟加载的工作方式时遇到了麻烦。 For example, in the following example, I can access the Courses of Students within Where() clause: 例如,在以下示例中,我可以在Where()子句中访问“ StudentsCourses ”:

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

However, the following will not work and will throw null exception if I do not use Include() although I did not disable Lazy Loading: 但是,如果我没有使用懒惰加载,尽管我不使用Include() ,以下内容将无法工作,并且将引发null异常:

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

Can someone explain me why it works this way? 有人可以解释一下为什么会这样吗?

In order for lazy loading to work, two things have to happen: 为了使延迟加载工作,必须发生两件事:

  1. Lazy Loading must be enabled on the context 必须在上下文中启用延迟加载
  2. The properties to be lazy-loaded must be virtual. 要延迟加载的属性必须是虚拟的。

In your case, you have explained that your property is not virtual, so it can't be lazy loaded. 对于您的情况,您已经说明了您的属性不是虚拟的,因此不能延迟加载。 However, lazy loading is only needed if you want to access child entities or collections after loading the base object from the database. 但是,仅当您要在从数据库加载基础对象之后访问子实体或集合时才需要进行延迟加载。 That means you are doing two very different things in your examples. 这意味着您在示例中所做的两件事非常不同。

In your first example, you've written an EF query that includes a call to IQueryable.Where , followed by IQueryable.ToList . 在第一个示例中,您编写了一个EF查询,其中包括对IQueryable.Where的调用,然后是IQueryable.ToList的调用。 When this code runs, EF is going to try to translate that Where call into a call to the underlying SQL data store. 当此代码运行时,EF将尝试将该Where调用转换为对基础SQL数据存储的调用。 Within that call, you access a child entity reference on the object being queried, so the EF expression parser sees this reference and knows to translate that into SQL as well. 该调用中,您访问要查询的对象上的子实体引用,因此EF表达式解析器可以看到该引用,并且知道也可以将其转换为SQL。 Essentially, you're only asking EF to make one database call, so it works. 本质上,您只要求EF进行一个数据库调用,所以它可以工作。

(One caveat: even though your first query works, the child collection is not populated when the call to ToList completes; the SQL query still only returns the fields needed to populate the top level object. All that happens is that EF includes the child table in the WHERE clause to filter the result set. If you tried to access the Courses property on any of those returned Student objects, it would still fail.) (一个警告:即使您的第一个查询成功,对ToList的调用完成后也不会填充 ToList ; SQL查询仍然只返回填充顶级对象所需的字段。所有发生的事情是EF包括了子表(在WHERE子句中)以过滤结果集。如果您尝试访问任何返回的Student对象的Courses属性,它仍然会失败。)

In your second example, you're making a call to IQueryable.Single to get a single student, followed by a call to the Courses property getter, followed by a call to IQueryable.ToList . 在第二个示例中,您要致电IQueryable.Single以获得一个学生,然后致电Courses属性getter,再致电IQueryable.ToList Again, the EF expression parser sees whatever's inside the Single method call and turns that into a SQL query, but your attempt to access the child collection happens outside that call. 同样,EF表达式解析器会看到Single方法调用中的内容,并将其转换为SQL查询,但是您尝试访问子集合的尝试是该调用之外进行的。 Here, you're asking EF to do two "queries": one to get the student, and one to get the courses. 在这里,您要EF进行两项“查询”:一项是让学生获得,另一项是获得课程。 Since lazy loading is not enabled, the second query never runs, and EF instead returns null immediately. 由于未启用延迟加载,因此第二个查询将永远不会运行,而EF会立即返回null This leads to an attempt to call ToList on a null object, which gives you the expected error. 这将导致尝试在null对象上调用ToList ,这将给您带来预期的错误。

If you had used Include in the second query, EF would have been forced to generate a different SQL query to satisfy your call to Single , one that included all the information needed to also populate the Courses child collection. 如果您在第二个查询中使用了Include ,则EF将不得不生成另一个SQL查询来满足您对Single的调用,该调用包含了也填充Courses所需的所有信息。 In that case, when you attempted to access Courses in the next step, it wouldn't be null , it would already be populated, and the ToList call would work. 在这种情况下,当您尝试在下一步中访问“ Courses时,它不会为null ,它已经被填充,并且ToList调用将起作用。

To really make sense of the difference, the easiest thing to do is just look at the SQL queries generated in each case; 要真正理解它们之间的区别,最简单的方法就是查看每种情况下生成的SQL查询。 there's a number of ways to do this, an easy one being described here: 有很多方法可以做到这一点,这里描述一个简单的方法:

How do I view the SQL generated by the Entity Framework? 如何查看由实体框架生成的SQL?

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

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