接上一篇《DbContext 查询(二)》数据库
Eager Loading spa
暂且称之为主动加载, 主动加载取决于你要告诉EF你想要加载哪些相关数据进内存,以后EF会在生成的SQL语句中使用JOIN来查询数据。让咱们看以下示例:查询全部Destinations以及相关的Loadings。rest
Example 2-24code
1 private static void TestEagerLoading()blog
17 } 排序
以上示例使用了Include方法代表查询返回的全部Destinations应该包含了相关的Lodging数据。Include使用lambda表达式来指明那个属性要包含在返回的数据中,你们查看MSDN会发现Include方法还有一个重载方法,接受的是一个字符串参数,在咱们的示例中这样写Include("Lodgings"),这个重载问题重重,因为参数不是强类型的因此编译器不能编译时检查正确与否,不推荐你们使用。内存
在单个查询也能够包含多个相关实体数据,好比咱们想查询Lodgings而且包含PrimaryContact外加关联的Photo。咱们能够这样下:ci
context.Lodgings.Include(p=>p.PrimaryContact.Photo); 字符串
再好比你想要查询Destinations并包含Lodgings外加Lodgings相关的PrimaryContact,咱们能够这么写:编译器
context.Destinations.Include(p=>p.Lodgings.Select(t=>t.PrimaryContact));
Include方法在同一个查询中也可调用屡次来指明加载不一样的数据:
context.Lodgings
.Include(p=>p.PrimaryContact)
.Include(p=>p.SecondaryContact);
关于Eager Loading的一些缺点
有上文得知,Eager Loading在生成的SQL中使用JOIN来进行查询,这会将之前须要多个查询语句才能获得的结果,到如今可能只须要一个查询语句就能够实现,但这并不老是好的。当你想要Include更多的数据,SQL语句中使用JOIN的次数也会更多,这会造成更慢以及更复杂的查询,若是你须要至关数量的关联数据,多个简单查询语句一般会比一个又大又复杂的查询语句更快。
在LINQ查询语句中使用Include
你也可在LINQ查询语句中使用Include,若是你使用query syntax:
var query = from d in context.Destinations.Include(d=>d.Lodgings)
where d.Country ==""
select d;
若是你使用method syntax,则能够这样写:
var query = context.Destinations
.Include(d=>d.Lodgings)
.Where(d=>d.Country=="");
Include是IQueryable<T>的扩展方法, 因此在查询的任何点均可以使用,并不须要当即就跟在DbSet以后,好比你想加载国家为澳大利亚的Destination的相关Lodgings:
var query = from d in context.Destinations
where d.Country = "Australia"
select d;
query = query.Include(d=>d.Lodgings);
记住Include并非修改原有查询,而是返回一个新的查询,同时咱们也强调过屡次,直到有代码访问查询的结果,不然EF不会执行查询,上面的这段代码并无使用查询返回的结果,全部EF不会执行任何查询。
Explicit Loading
第三个加载选项是Explicit Loading。Explicit Loading相似于Lazy Loading(相关联数据是分开加载的)。当主数据被加载完,不一样于Lazy Loading,Explicit Loading不会自动为你加载相关数据,你须要手动调用一个方法。
下面列出了你会使用Explicit Loading而不是Lazy Loading的一些缘由:
Explicit Loading是使用DbContext.Entry方法来实现的。Entry方法让你能够操做DbContext内实体的全部信息。除了能够访问存储在实体内的当前实体的信息,还能够访问实体的状体以及从数据库返回的原始实体值的信息。Entry方法还可让你对实体调用一些操做,包括为导航字段加载数据。
一旦咱们获取了一个实体,咱们就可使用Collection和Reference方法来查看实体导航字段的信息以及操做导航字段的方法。Load方法就是其中一个操做方法,而它的用法上篇博客都有讲到,这里就再也不赘述。
让咱们来用Explicit Loading来一样的加载名称为Grand Canyon的Destination的关联属性Lodgings:
Example 2-25
1 private static void TestExplicitLoading()
上面代码中,前半部分是普通的LINQ查询语句,以后调用了Entry方法,传递了canyon实例,而后调用Collection方法来操做到Lodgings导航属性,而后加载。Collection和Reference使用lambda表达式做为参数传递,相似于Include方法他们同时也有字符串参数的重载方法,孰优孰劣就再也不赘述了!
若是你运行上面代码,并监控数据库查询语句,你会看到两个查询: 一个执行在代码请求名称为Grand Canyon的Destination的单个结果(Single)时,另外一个运行在Load方法调用时。
你能够看到Explicit Loading可使用在加载集合导航字段的全部内容上,而它也可使用在加载一部份内容上(经过LINQ 查询)。
Explicit Loading(显式加载)一个导航字段(非集合)看起来很是相似,只不过调用方法变成了Reference:
var lodging = context.Lodgings.First();
context.Entry(lodging)
.Reference(p=>p.PrimaryContact)
.Load();
验证导航字段是否被加载了
调用Reference以及Collection方法以后呢,你能够访问IsLoaded属性。IsLoaded会告诉你导航字段的全部内容是否从数据库加载了。这个属性在咱们使用Lazy、Eager、Explicit Loading来加载导航字段实体的时候会被设置为True。
Example 2-26
1 private static void TestIsLoaded()
16 }
上面示例代码运行以后一目了然:第一次打印是False,由于尚未使用任何一种加载模式加载导航属性实体,第二次打印就变为True了。
若是你在使用Explicit Loading,若是导航属性内容可能已经被加载了,你就可使用IsLoaded来决定是否要加载。
查询集合导航属性的内容
到如今为止你已经知道了如何加载全部集合导航属性的内容,这样你就能够在内存中操做数据(LINQ to Object 筛选、排序等),若是你只对集合导航属性的一部份内容感兴趣,你能够只把这部分数据加载到内存中,或者你只是想要计算数量,或别的一些计算操做,你只须要把计算结果加载到内存中。
一旦你使用了Entry和Collection方法来切入到集合导航属性,以后你就可使用Query方法来得到一个LINQ查询(导航属性的内容)。由于这是一个LINQ查询,以后你就能再进行筛选、排序、汇集等操做。
假设你想要找到距离最近的机场少于10英里的 名称为Grand Canyon的Destination相关的Lodgings。你能够写以下示例:
Example 2-27 (内存内查询导航属性)
1 private static void QueryLodgingDistance()
24 }
上面这段代码的问题在于使用LINQ to Object来查询Lodgings导航属性内容。这回致使这个属性被Lazy Loading,加载全部数据到内存中。代码以后又对数据进行了筛选,意味着并不须要加载全部数据进内存。让咱们重写这段代码:Example 2-27:
1 private static void QueryLodgingDistance()
24 }
更新后的这段代码使用了Query方法来为Grand Canyon相关的Lodgings建立LINQ to Entities查询,而后对这查询进行筛选。下面foreach遍历distanceQuery时EF执行SQL语句转换并对MilesFromNearsAirport在数据库中进行筛选。这就意味着只有你所须要的数据被加载进了内存。
也许你想知道名称为Grand Canyon的Destinations有多少个Lodging。你能够加载全部的Lodgings而后得到个数,但为何不只仅只是得到一个单一的数字结果而无需加载全部数据呢,看以下示例:
Example 2-29
1 private static void QueryLodgingCount()
18 }
以上代码无需过多解释,Query方法返回的是LINQ to Entities查询,它意识到你只是须要数量而后把全部查询推到数据库端,因此只有一个简单的数字从数据库返回了
Explicit Loading 导航属性内容的子集
你能够同时使用Query以及Load方法来进行筛选以后的显式加载(filtered explicit load),这个explicit loading仅仅加载导航属性内容的子集,好比你想要仅仅加载名称为Grand Canyon的Destination的相关的Lodging以及这个相关的Lodging的名称中包含“Hotel”的数据:
context.Entry(canyon)
.Collecction(p=>p.Lodgings)
.Query()
.Where(l=>l.Name.Contains("Hotel"))
.Load();
至此关于DbContext查询相关的功能基本探讨完了,后续博客咱们继续探讨下对实体的增删改的基本操做。