19

我发现我很困惑延迟加载等EF:延迟加载,渴望装载和“枚举枚举”

首先,是这两个语句等效:

(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发展,顺便......虽然这些问题可以应用更普遍。)

+0

EntityFramework使用预先加载/延迟加载(加载数据的通用软件术语)来提取相关对象和集合。被缓存的查询执行是LINQ的一个工件,它允许查询更加灵活(在某些情况下,它们可能不会被执行)。 – jwize 2014-11-18 07:26:18

回答

17

你的说明(1)是正确的,但它是一个例子Eager Loading而不是延迟加载。

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

无论哪种情况,只要您尝试使用_flaggedDates“执行某些操作”,就会发现没有数据会从数据存储区加载。但是,发生的情况在每种情况下都不相同。 (1):急切的加载:只要你开始你的for循环,你指定的每一个对象都将从数据库中被拉出来,并被构建成一个巨大的内存数据结构。这将是一项非常昂贵的操作,从数据库中提取大量数据。但是,它将全部发生在一次数据库往返中,并且执行单个SQL查询。

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

效应初探以评论

禁用延迟加载是不一样的启用预先加载。在这个例子中:

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

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

您确定where d.dateID == 2意味着只有与该特定FlaggedDate对象相关的对象才会被拉入。但是,根据与该FlaggedDate相关的对象的数量,您仍可能获得大量数据通过电线。这是由于EntityFramework构建SQL查询的方式。 SQL查询结果始终为表格格式,这意味着每行必须具有相同的列数。对于每个scheduledSchool对象,结果集中至少需要有一行,并且由于每行必须至少包含的某些值,因此最终将重复标记FlaggedDate对象上的每个标量值。因此,如果您有10个预定的学校和10个与您的FlaggedDate相关的访谈,则最终会有20个行,每个行包含FlaggedDate上的每个标量值。一半的行对于所有ScheduledSchool列将具有空值,而另一半对于所有的访问列将具有空值。

但是,如果这样做会变得非常糟糕,那就是如果您对包含的数据“深入”了解。例如,如果每个ScheduledSchool都有一个students属性(您也包括在内),那么突然之间,每个ScheduledSchool中的每个学生都会有一行,并且在每个这些行上,都会包含Student's ScheduledSchool的每个标量值(甚至尽管只有第一行的值最终被使用)以及原始FlaggedDate对象上的每个标量值。它可以快速加起来。

这很难以书面形式解释,但是如果您查看多个Include的查询返回的实际数据,则会看到有很多重复的数据。您可以使用LinqPad查看由EF代码生成的SQL查询。

+0

对于(1)(MY(1),我的意思是懒惰加载已启用。该语句是一个加载的例子,因为包含..对吗? – Cynthia 2010-09-16 20:42:25

+0

对于(2)(MY(2))...如果延迟加载是DISABLED(换句话说,热切加载是有效的),你是说导航属性不会与FlaggedDates一起加载?那么加载意味着什么? – Cynthia 2010-09-16 20:44:00

+0

对你的编辑做出的反应:OK,我明白你在说什么了。但是,对于你的观点#1:所有的对象将被拉入,但只有在你的查询中指定的,对吗?例如,在我的例子中,我说d.dateID == 2,所以只有FlaggedDate对象的对象使用dateID = 2会将数据拉入。对吗?只要你限制了查询的范围,那么它就不会那么贵。 – Cynthia 2010-09-16 20:51:01

0

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

有一点要记住的是,这些包括可以去灰飞烟灭,如果您在多个对象查询,像这样:

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

ObjectDate.MyObjectProperty不会被自动加载。