翻译的初衷以及为何选择《Entity Framework 6 Recipes》来学习,请看本系列开篇html
实体框架提供了很是棒的建模环境,它容许开发人员可视化地使用映射到数据库中的表、视图、存储过程以及关系中的实体类型。本节将向你展现如何控制查询操做中的关联实体的加载。数据库
实体框架的默认行为是只加载应用程序直接须要的实体。一般状况下,这正是你须要的。若是实体框架经过一个或多个关联积极地加载关联实体,最终,你颇有可能获得超过你需求的实体。这不但增长了内存占用,并且还影响了应用程序的性能。框架
在实体框架中,当加载关联实体时,你能控制并优化数据库查询执行的次数。若是在加载关联对象时精心管理的话,能提供应用程序的性能,以及对数据有更多的控制。异步
在本章,咱们将演示加载关联数据的各类有效选项,并讲述他们的优缺点。咱们会特别地讨论实体框架的默认行为 Lazy Loading(延迟加载)以及它的真正含义。而后,咱们将演示在一个单独查询中,部分或者所有加载关联实体的选项。这种类型的加载,叫作Eager Loading(预先加载),它既被用来减小数据交互,也被用来控制加载哪一个关联实体。工具
有的时候,你想延缓加载某一关联实体,由于加载它的成本过高或者不常用。对于这种状况,咱们将介绍另外一种加载关联实体的方法,它叫作Explicit Loading(显示加载)。同时演示使用Load()方法来精确控制什么时候加载一个或多个关联实体的多种场景。性能
最后,咱们将简要地看看异步操做。学习
问题优化
你想加载一个实体,并在应用程序须要时才加载关联实体。spa
解决方案翻译
假设你有如图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,下面的查询将不会从新去数据库查询,而是返回以前查询的内存数据
//注:原书中的注释有些模糊,本行是我(付灿)增长的说明,这里还要去查询Customer表一次,可是它的关联属性,不会再去查询数据库了,而是直接从内存返回以前查询出来的对象 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) jsmith@gmail.com joan@smith.com Bill Meyers is a Retail Customer, email address(es) bmeyers@gmail.com Joan Smith is a Web Customer, email address(es) jsmith@gmail.com joan@smith.com Bill Meyers is a Retail Customer, email address(es) bmeyers@gmail.com
原理
默认状况下,实体框架只加载肯定须要使用的实体。这就是所谓的延迟加载,这是须要记住的一条很重要的准则。相反,加载父类和全部关联的实体,这叫作Eager Loading(预先加载),它会可能会加载一个大的对象图,这远远超出了你的需求。且不说获取对象图的开销,封送,实例化。
在示例中,咱们最早给出一个查询,它查询出全部的customers。有趣的是,这个查询也不是当即执行的,它是在第一个foreach语句开始枚举Customer实体时才被执行。这个延迟加载的行为是LINQ带来的。
在第一个foreach中,咱们只须要Custome表中的数据,不须要CustomerType和CustomerEmail表中的数据。在这种状况下,实体框架只查询Customer表,不去查询与 Customer表关联的CustomerType和CustomerEmail表。
而后,在第二个foreach中,咱们显式引用了CustomerType实体中的Description属性和CustomerEmail实体中的Email属性。在实体框架中,直接访问这些属性,会为每一个关联的表生成一个查询。这对理解实体框架在关联表第一次被访问时单独生成查询很重要。一旦经过关联实体调用了查询,实体框架会将该属性标记为已加载(loaded),并在以后对该属性的访问中直接从内存返回数据,再也不一次又一次地查询数据库。咱们在这个示例中,一共为子数据(译注:关联实体数据)生成了4个单独的查询:
一、针对Joan Smith的CusotrmType和CustomerEmail的两条Select语句;
二、针对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/