翻譯的初衷以及爲何選擇《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/