Entity Framework加载相关实体——Lazy Loading
Entity Framework提供了三种加载相关实体的方法:Lazy Loading,Eager Loading和Explicit Loading。首先我们先来看一下MSDN对三种加载实体方法的定义。
Lazy Loading:对于这种类型的加载,在您访问导航属性时,会从数据源自动加载相关实体。 使用此加载类型时,请注意,如果实体尚未在 ObjectContext 中,则您访问的每个导航属性都会导致针对数据源执行一个单独的查询。
Eager Loading:当您了解应用程序需要的相关实体的图形的确切形状时,可以使用 ObjectQuery 的 Include 方法来定义查询路径,此查询路径控制将哪些相关实体作为初始查询的一部分返回。 当定义查询路径时,仅需对数据库请求一次,即可在单个结果集中返回查询路径所定义的所有实体,并且属于在路径中定义的类型的所有相关实体将随查询返回的每个对象一起加载。
Explicit Loading:将实体显式加载到 ObjectContext 需要多次往返数据库,并且可能需要多个活动结果集,但是返回的数据量仅限于所加载的实体。 可以对 EntityCollection或 EntityReference 使用 Load 方法或对 ObjectContext 使用 LoadProperty 方法,以便从数据源显式检索相关实体。 对于 Load 方法的每个调用都会打开与数据库的连接,以检索相关信息。 这可确保在没有对相关实体的显式请求时,始终不会执行查询。
下面我们就以上三种加载方式来一一进行测试
在测试之前,我们先建立一个测试用的数据库,并在其中插入一些数据:
图1
Lazy Loading
在Entity Framework4.0及其以后版本,LazyLoading是默认打开的,从数据库生成Model后,我们可以在EDMX文件空白处单击,并在属性窗口看到这一设置:
图2
也可以以XML形式打开EDMX文件,在CSDL部分看到这一设置:
1 <!-- CSDL content -->
2 <edmx:ConceptualModels>
3 <Schema Namespace="TestModel" Alias="Self" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
4 <EntityContainer Name="TestEntities" annotation:LazyLoadingEnabled="true">
5 <EntitySet Name="Players" EntityType="TestModel.Player" />
6 <EntitySet Name="PlayerDetails" EntityType="TestModel.PlayerDetail" />
7 <EntitySet Name="Teams" EntityType="TestModel.Team" />
8 <AssociationSet Name="FK_Player_Team1" Association="TestModel.FK_Player_Team1">
9 <End Role="Team" EntitySet="Teams" />
10 <End Role="Player" EntitySet="Players" />
11 </AssociationSet>
12 <AssociationSet Name="FK_PlayerDetails_Player" Association="TestModel.FK_PlayerDetails_Player">
13 <End Role="Player" EntitySet="Players" />
14 <End Role="PlayerDetails" EntitySet="PlayerDetails" />
15 </AssociationSet>
16 </EntityContainer>
注意:Lazy Loading的设置是针对所有Model的,并非某一个Model。
下面,我们写一段简单的代码来测试一下Lazy Loading:
using (var context = new TestEntities())
{
IQueryable<Team> teams = from t in context.Teams select t;
foreach (Team t in teams)
{
Console.WriteLine(t.Players.Count());
}
Console.Read();
}
运行后的结果如下图:
图3
我们可以看到,在query语句中,我们只是要求返回所有的team信息,并没有像数据库请求加载player的信息,但在Foreach语句中,我们要求打印出每支team的player数量,却成功了,这就是Lazy Loading实现的效果。实际上,当执行Count语句时,程序会再去取请求数据库,返回player信息,这也就是说,如果我们有100支球队,程序会访问100次数据库来执行此操作。
下面我们关闭Lazy Loading来看看效果。关闭Lazy Loading有多种方法,我们可以在图2的属性窗口直接将Lazy Loading Enabled设置为False,也可以在XML代码中将Lazy Loading Enabled赋值False,以下我们用程序代码来关闭Lazy Loading并执行上面代码来看一下效果:
using (var context = new TestEntities())
{
//Disable Lazy Loading
context.ContextOptions.LazyLoadingEnabled = false;
IQueryable<Team> teams = from t in context.Teams select t;
foreach (Team t in teams)
{
Console.WriteLine(t.Players.Count());
}
Console.Read();
}
执行结果如下:
图4
从执行结果我们可以看到,当执行Foreach语句时,程序并没有去查询数据库,而我们的query语句又没有向数据库请求关于player的信息,故无法打印出player的数量。
最后我们来总结一下Lazy Loading的优势和劣势:当打开Lazy Loading时,我们可以不用去在意某实体是否已经加载,不会出现在调用某一实体时,出现null的尴尬,省去程序员不少心力,但同时劣势也非常明显,如果我们有大量实体,且频繁去调用相关实体,程序就会频繁地访问数据库,这很显然地会影响程序的性能。
下一次我们会来分析关闭Lazy Loading的情况下,如何显示加载相关实体,即Explicit Loading。