接上一篇《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查詢相關的功能基本探討完了,後續博客咱們繼續探討下對實體的增刪改的基本操做。