EF:延迟加载,急切加载和“枚举可枚举”

时间:2010-09-16 20:30:38

标签: entity-framework entity-framework-4 lazy-loading eager-loading

我发现我对延迟加载等感到困惑。

首先,这两个陈述是否相同:

(1) Lazy loading:
_flaggedDates = context.FlaggedDates.Include("scheduledSchools")
.Include  ("interviews").Include("partialDayAvailableBlocks")
.Include("visit").Include("events");

(2) Eager loading:
_flaggedDates = context.FlaggedDates;

换句话说,在(1)中,“包含”会导致导航集合/属性与请求的特定集合一起被加载,而不管您是否使用延迟加载......对吗?

在(2)中,语句将加载所有导航实体,即使您没有特别请求它们,因为您正在使用预先加载...对吗?

第二:即使您正在使用预先加载,在您“枚举可枚举”之前,数据实际上不会从数据库中下载,如下面的代码所示:

var dates = from d in _flaggedDates
            where d.dateID = 2
            select d;
foreach (FlaggedDate date in dates)
{
... etc.
}

在foreach循环之前,实际上不会下载(“枚举”)数据......对吗?换句话说,“var dates”行定义了查询,但是在foreach循环之前不会执行查询。

鉴于(如果我的假设是正确的),渴望加载和延迟加载之间的真正区别是什么?似乎在任何一种情况下,数据在枚举之前都不会出现。我错过了什么吗?

(我的具体经验是代码优先,POCO开发,顺便说一下......虽然这些问题可能更普遍适用。)

2 个答案:

答案 0 :(得分:17)

您对(1)的描述是正确的,但它是Eager Loading而不是延迟加载的示例。

您对(2)的描述不正确。 (2)技术上根本不使用加载,但如果你试图访问FlaggedDates上的任何非标量值,将使用延迟加载。

在任何一种情况下,您都是正确的,在您尝试使用_flaggedDates“执行某些操作”之前,不会从数据存储中加载任何数据。但是,在每种情况下会发生什么不同。

(1):渴望加载:一旦开始for循环,您指定的每个对象都将从数据库中拉出并构建到一个巨大的内存数据结构中。这将是一项非常昂贵的操作,从您的数据库中提取大量数据。但是,它将全部发生在一个数据库往返中,执行单个SQL查询。

(2):延迟加载:当你的for循环开始时,它只会加载FlaggedDates对象。但是,如果访问for循环内的相关对象,则它们不会将这些对象加载到内存中。为给定的FlaggedDate检索scheduledSchools的第一次尝试将导致新的数据库往返以检索学校,或者因为您的上下文已被处理而抛出异常。由于您将在for循环内访问scheduledSchools集合,因此对于您在for循环开始时最初加载的每个FlaggedDate,您将有一个新的数据库往返。

回应评论

禁用延迟加载与启用预先加载不同。在这个例子中:

context.ContextOptions.LazyLoadingEnabled = false;
var schools = context.FlaggedDates.First().scheduledSchools;

schools变量将包含一个空的EntityCollection,因为我在原始查询中没有Include它们(FlaggedDates.First()),并且我禁用了延迟加载以便它们不能在初始查询执行后加载。

你是正确的where d.dateID == 2意味着只会引入与特定FlaggedDate对象相关的对象。但是,根据与FlaggedDate相关的对象数量,你仍然可以得到一个很多数据都通过这条线。这是由于EntityFramework构建其SQL查询的方式。 SQL查询结果始终采用表格格式,这意味着每行必须具有相同的列数。对于每个scheduledSchool对象,结果集中至少需要有一行,并且由于每一行必须至少包含每列的某些值,因此最终会得到FlaggedDate上的每个标量值对象被重复。因此,如果你有10个预定学校和10个与你的FlaggedDate相关的访谈,你最终会得到20行,每行包含FlaggedDate上的每个标量值。所有ScheduledSchool列的一半行将具有空值,而所有Interviews列的另一半将具有空值。

如果你真的很糟糕,那就是你在你所包含的数据中“深入”了。例如,如果每个ScheduledSchool都有一个students属性,您也可以包含该属性,那么每个ScheduledSchool中的每个学生都会突然出现一行,并且在每个行上,学生的ScheduledSchool的每个标量值都会包括(即使只有第一行的值最终被使用),以及原始FlaggedDate对象上的每个标量值。它可以快速加起来。

很难以书面形式解释,但是如果你查看从具有多个Include的查询返回的实际数据,你会发现存在大量重复数据。您可以使用LinqPad查看EF代码生成的SQL查询。

答案 1 :(得分:0)

没有区别。在EF 1.0中不是这样,它不支持急切加载(至少不自动加载)。在1.0中,您必须修改属性以自动加载,或者在属性引用上调用Load()方法。

要记住的一件事是,如果您跨多个对象查询,那些包含可能会冒烟:

from d in ctx.ObjectDates.Include("MyObjectProperty")
from da in d.Days

不会自动加载ObjectDate.MyObjectProperty。

相关问题