《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载

翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇

第五章 加载实体和导航属性

  实体框架提供了非常棒的建模环境,它允许开发人员可视化地使用映射到数据库中的表、视图、存储过程以及关系中的实体类型。本节将向你展示如何控制查询操作中的关联实体的加载。

  实体框架的默认行为是只加载应用程序直接需要的实体。通常情况下,这正是你需要的。如果实体框架通过一个或多个关联积极地加载关联实体,最终,你很有可能得到超过你需求的实体。这不但增加了内存占用,而且还影响了应用程序的性能。

  在实体框架中,当加载关联实体时,你能控制并优化数据库查询执行的次数。如果在加载关联对象时精心管理的话,能提供应用程序的性能,以及对数据有更多的控制。

  在本章,我们将演示加载关联数据的各种有效选项,并讲述他们的优缺点。我们会特别地讨论实体框架的默认行为 Lazy Loading(延迟加载)以及它的真正含义。然后,我们将演示在一个单独查询中,部分或者全部加载关联实体的选项。这种类型的加载,叫做Eager Loading(预先加载),它既被用来减少数据交互,也被用来控制加载哪个关联实体。

  有的时候,你想延缓加载某一关联实体,因为加载它的成本太高或者不经常使用。对于这种情况,我们将介绍另一种加载关联实体的方法,它叫做Explicit Loading(显示加载)。同时演示使用Load()方法来精确控制何时加载一个或多个关联实体的多种场景。

  最后,我们将简要地看看异步操作。

5-1  延迟加载关联实体

问题

  你想加载一个实体,并在应用程序需要时才加载关联实体。

解决方案

  假设你有如图5-1所示的模型。

图5-1 包含Customer和与它相关联信息的实体

  在模型中,有一个Customer实体,一个与它关联的CustomerType和多个与它关联的CustomerEamil。它与CustomerType的关系是一对多关系,这是一个实体引用(译注:Customer中的导航属性CustomerType)。

  Customer与CustomerEmail也是一对多关系,只是这时CustomerEmail在多的这一边。这是一个实体集合(译注:Customer中的导航属性CustomerEmails)。

  当把它们三个实体放在一起时,就得到一个叫做对象图(object graph)的结构。一个对象图包含单个且与之关联的实体,它们构成一个整体的逻辑单元。特别地,一个对象图是一个给定实体和与之关联实体在某一特定时刻上的视图。例如,在我们的应用操作中有一个ID为5,Name为“John Smith“的customer,它有一个类型为“perferred"的CustomerType,和一个包含10个CustomerEmails的集合。

  代码清单5-1.演示了实体框架加载关联实体的默认行为,Lazy Loading(延迟加载)。

代码清单5-1.  延迟加载Customer的关联实体CustomerType和CustomerEmail的实例

 1  using (var context = new EFRecipesEntities())
 2             {
 3                 var customers = context.Customers;
 4                 Console.WriteLine("Customers");
 5                 Console.WriteLine("=========");
 6
 7                 // 只需要Customer实体的信息
 8                 foreach (var customer in customers)
 9                 {
10                     Console.WriteLine("Customer name is {0}", customer.Name);
11                 }
12
13
14                 //现在,需要使用到关联实体CustomerType和CustomerEamil的信息,
15                 //实体框架为每个实体对象产生一个单独的查询来获取关联实体的信息。
16                 foreach (var customer in customers)
17                 {
18                     Console.WriteLine("{0} is a {1}, email address(es)", customer.Name,
19                                       customer.CustomerType.Description);
20                     foreach (var email in customer.CustomerEmails)
21                     {
22                         Console.WriteLine("\t{0}", email.Email);
23                     }
24                 }
25
26                 // Extra credit:
27                 //如果你打开Sql Server Profiler,下面的查询将不会重新去数据库查询,而是返回之前查询的内存数据
28                 foreach (var customer in customers)
29                 {
30                     Console.WriteLine("{0} is a {1}, email address(es)", customer.Name,
31                                       customer.CustomerType.Description);
32                     foreach (var email in customer.CustomerEmails)
33                     {
34                         Console.WriteLine("\t{0}", email.Email);
35                     }
36                 }
37             }

代码清单5-1的输出如下:

Customers
=========
Customer name is Joan Smith
Customer name is Bill Meyers
Joan Smith is a Web Customer, email address(es)
[email protected]
[email protected]
Bill Meyers is a Retail Customer, email address(es)
[email protected]
Joan Smith is a Web Customer, email address(es)
[email protected]
[email protected]
Bill Meyers is a Retail Customer, email address(es)
[email protected]

原理

  默认情况下,实体框架只加载确定需要使用的实体。这就是所谓的延迟加载,这是需要记住的一条很重要的准则。相反,加载父类和所有关联的实体,这叫做Eager Loading(预先加载),它会可能会加载一个大的对象图,这远远超出了你的需求。且不说获取对象图的开销,封送,实例化。

  在示例中,我们最先给出一个查询,它查询出所有的customers。有趣的是,这个查询也不是立即执行的,它是在第一个foreach语句开始枚举Customer实体时才被执行。这个延迟加载的行为是LINQ带来的。

  在第一个foreach中,我们只需要Custome表中的数据,不需要CustomerType和CustomerEmail表中的数据。在这种情况下,实体框架只查询Customer表,不去查询与 Customer表关联的CustomerType和CustomerEmail表。

  然后,在第二个foreach中,我们显式引用了CustomerType实体中的Description属性和CustomerEmail实体中的Email导入属性。在实体框架中,直接访问这些属性,会为每个关联的表生成一个查询。这对理解实体框架在关联表第一次被访问时单独生成查询很重要。一旦通过关联实体调用了查询,实体框架会将该属性标记为已加载(loaded),并在之后对该属性的访问中直接从内存返回数据,不再一次又一次地查询数据库。我这个示例中,一共为子数据(译注:关联实体数据)生成了4个单独的查询:

    1、针对Joan Smith的CusotrmType和CustomerEmail的两条Select语句;

    2、针对Bill Meyers的CusotrmType和CustomerEmail的两条Select语句;

  当用户在应用中按他们的需求浏览不同的数据进,这些针对每个子表的查询工作得很好,它们能提高应用的响应时间。因为使用一系列按需获取数据的简短的查询。相反,如果使用预先加载大量数据的方式,可能导致视图加载迟缓。

  然后,在你需要大量使用关联表数据时,这个方法不是你看到的这么有效。在这种情况下,Eager Loading(预先加载)可能是一种更好的选择,它在一个单独的查询中,从父表和与之关联的表中获取所有的数据。

  最后一部分代码块,名为‘Extra Credit‘,演示了子属性如果已经加载,实体框架会从内存中返回数据,而不是重新查询数据库。打开SQL Server Profiler工具,运行该示例,你会发现,在‘Extra Credit‘部分,当子属性被引用时,代码块没有生成SQL语句。

注意  SQL Server Profiler是一个检查(监视)SQL Server中SQL语句非常棒的工具,它是免费的,包含在SQL Server Devoloper Edition和更高级的版本中:http://technet.microsoft.com/en-us/library/ms181091.aspx

  

实体框架交流QQ群:  458326058,欢迎有兴趣的朋友加入一起交流

谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/

时间: 2024-08-10 05:55:51

《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载的相关文章

《Entity Framework 6 Recipes》中文翻译系列 (28) ------ 第五章 加载实体和导航属性之测试实体是否加载与显式加载关联实体

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-11  测试实体引用或实体集合是否加载 问题 你想测试关联实体或实体集合是否已经加载到上下文中,另外你想使用Code-First来管理数据访问. 解决方案 假设你有如图5-26所示的概念模型 图5-26 一个包含projects,managers和contractors的模型 在Visual Studio中添加一个名为Recipe11的控制台应用,并确保引用了实体框架6的库,NuGet可

《Entity Framework 6 Recipes》中文翻译系列 (23) -----第五章 加载实体和导航属性之预先加载与Find()方法

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-2  预先加载关联实体 问题 你想在一次数据交互中加载一个实体和与它相关联实体. 解决方案 假设你有如图5-2所示的模型. 图5-2 包含Customer和与它相关联信息的实体 和5-1节一样,在模型中,有一个Customer实体,一个与它关联的CustomerType和多个与它关联的CustomerEamil.它与CustomerType的关系是一对多关系,这是一个实体引用(译注:Cu

《Entity Framework 6 Recipes》中文翻译系列 (26) ------ 第五章 加载实体和导航属性之延缓加载关联实体和在别的LINQ查询操作中使用Include()方法

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-7  在别的LINQ查询操作中使用Include()方法 问题 你有一个LINQ查询,使用了类似这样的操作 group by,join,和where:你想使用Include()方法预先加载额外的实体.另外你想使用Code-First来管理数据访问. 解决方案 假设你有如图5-22所示的概念模型 图5-22 一个简单的包含Club和Event以及它们之间一对多关联的模型 在Visual S

《Entity Framework 6 Recipes》中文翻译系列 (25) ------ 第五章 加载实体和导航属性之加载完整的对象图和派生类型上的导航属性

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-5  加载完整的对象图 问题 你有一个包含许多关联实体的模型,你想在一次查询中,加载完整的对象图实例.一般地,当一个页面视图需要呈现关联实体集时,你会选择这种方法,而不是延迟加载,因为延迟加载是通过一系列的短小查询来获取关联实体的. 解决方案 假设你有如图5-20所示的概念模型.每门课程有很多节,每一节由一个老师教多名学习. 图5-20 一个包含许多关联实体的模型 使用Include()

《Entity Framework 6 Recipes》中文翻译系列 (29) ------ 第五章 加载实体和导航属性之过滤预先加载的实体集合和修改外键关联

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-13  过滤预先加载的实体集合 问题 你想过滤预先加载的实体集合,另外,你想使用Code-First来管理数据访问 解决方案 实体框架不支持直接使用Include()时过滤关联实体集合,但我们可以通过创建一个匿名类型来完成同样的事情,匿名类型包含实体和要过滤的关联实体集合. 假设你有如图5-28所示的概念模型 图5-28 一个包含movies(电影)和它的categories(目录)的模

《Entity Framework 6 Recipes》中文翻译系列 (27) ------ 第五章 加载实体和导航属性之关联实体过滤、排序、执行聚合操作

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-9  关联实体过滤和排序 问题 你有一实体的实例,你想加载应用了过滤和排序的相关实体. 解决方案 假设你有如图5-24所示的概念模型 图5-24 一个酒店预定系统的模型 假设我们有一个酒店(Hotel)实体,使用代码清单5-22,获取酒店的商务套房(executive suite),查看是否被预定,并按房价排序. 代码清单5-22.通过方法Entry()和Query()显式加载实体集合,

《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述 (转)

微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF版本更新太快,没人愿意去花时间翻译国外关于EF的书籍.使用Entity Framework开发已经有3年多了,但用得很肤浅,最近想深入学习,只好找来英文书<Entity Framework 6 Recipes>第二版,慢慢啃.首先需要说明的是,我英文不好,只是为了学习EF.把学习的过程写成博客,一

《Entity Framework 6 Recipes》翻译系列 (4) -----第二章 实体数据建模基础之从已存在的数据库创建模型 (转)

不知道对EF感兴趣的并不多,还是我翻译有问题(如果是,恳请你指正),通过前几篇的反馈,阅读这个系列的人不多.不要这事到最后成了吃不讨好的事就麻烦了,废话就到这里,直奔主题. 2-2 从已存在的数据库创建模型 问题 有一个存在的数据库,它拥有表.也许还有视图.外键.你想通过它来创建一个模型. 解决方案 让我们设想,你拥有一个描述诗人(Poet)以及他们的诗(Poem),还有他们之间关系的数据库.如图2-7所示. 图2-7 一个关于诗人及他们的诗的简单数据库 从上图可以看出,一个诗人可能是一首或多首

《Entity Framework 6 Recipes》翻译系列(2) -----第一章 开始使用实体框架之使用介绍 (转)

Visual Studio 我们在Windows平台上开发应用程序使用的工具主要是Visual Studio.这个集成开发环境已经演化了很多年,从一个简单的C++编辑器和编译器到一个高度集成.支持软件开发整个生命周期的多语言环境. Visual Studio以及它发布的工具和服务提供了:设计.开发.单元测试.调试.软件配置和管理.构建管理和持续集成等等.很少有开发人员因为还没有使用它而担心(注:作者应该是表达不用担心VS的能力),Visual Studio是一个完整的工具集.Visual Stu