Entity Framework技巧系列之二 - Tip 6 - 8
提示6. 如何及何時(shí)使用貪婪加載
什么時(shí)候你需要使用貪婪加載?
????通常在你的程序中你知道對(duì)查詢到的實(shí)體將要進(jìn)行怎樣的操作。
例如,如果你查詢一個(gè)訂單以便為一個(gè)客戶重新打印,你知道沒(méi)有組成訂單的項(xiàng)目即產(chǎn)品的信息重打印將是不完整的,所以你知道你將需要同時(shí)加載這些信息。
????這是貪婪加載起作用的一類場(chǎng)景。
如果你知道你需要額外信息,或?qū)嶓w,你可能也會(huì)預(yù)先加載這些實(shí)體(貪婪加載),因?yàn)檫@將省下生在將來(lái)的查詢。
怎樣進(jìn)行貪婪加載?
與一些普遍存在的錯(cuò)誤觀念相反,Entity Framework中貪婪加載即可行也易行,你僅使用Include()方法如下這樣啟動(dòng)查詢即可:
1 var reprint = (from order in ctx.Orders.Include("Items.Product") 2 where order.Customer.Name == "Fred Blogs" 3 && order.Status == "Unshipped" 4 select order).First();這個(gè)查詢意思是,在每個(gè)滿足查詢條件的order中包含其"Items",同時(shí)每個(gè)Item中也包含其"Product".
這樣的結(jié)果是,如下代碼:
1 foreach (var item in reprint.Items) 2 { 3 Console.WriteLine("\t{0} {1} = ${2}", 4 item.Quantity, 5 item.Product.Name, 6 item.Cost); 7 } 8 Console.WriteLine(reprint.TotalCost);不再需要新的查詢。
注意:
不需要顯式?Include("Items")?,對(duì)?Include("Items.Product")?的調(diào)用會(huì)隱式包含Items。
?
提示7 – 怎么在.NET 3.5 SP1中模擬外鍵屬性
背景
如果你一直關(guān)注EF Design Blog這個(gè)團(tuán)隊(duì)博客,你可能已經(jīng)看到近期我們宣布了一個(gè)用于.NET 4.0中EF的稱為FK Associations的特性。
然而在.NET 3.5 SP1中,我們僅支持獨(dú)立關(guān)聯(lián)。這表示外鍵列不會(huì)做為可用的屬性出現(xiàn)在實(shí)體中。相反這表示你不得不通過(guò)到其它實(shí)體的引用來(lái)建立關(guān)聯(lián)。
例如,不同于Linq to SQL,下面的寫(xiě)法在EF中不可用:
1 product.CategoryID = 2;因?yàn)樵趐roduct實(shí)體中沒(méi)有"CategoryID"這個(gè)屬性。
你不得不使用類似下面的替換方法:
1 product.Category = ctx.Categories.First(c => c.ID == 2);也就是你需要使用Category引用。這意味著你不得不在內(nèi)存中放置一個(gè)Category對(duì)象,或者像上面例子那樣通過(guò)查詢獲取一個(gè)。
不存在一個(gè)你可以直接設(shè)置CategoryID的屬性的問(wèn)題可能造成很大的麻煩,而這就是我們?cè)?NET 4.0中添加了FK Associations與FK Properties的原因。
這看起來(lái)都不錯(cuò),但Julie Lerman總習(xí)慣提醒我,現(xiàn)在正在使用.NET 3.5 SP1的人們?cè)趺崔k?我們的新特性現(xiàn)在幫不上忙。
所以…
怎樣在CategoryID不實(shí)際存在的情況下設(shè)置它?
這問(wèn)題是個(gè)圈套。你當(dāng)然實(shí)現(xiàn)不了這種想法。
但是你可以使用下面的代碼實(shí)現(xiàn)同樣的效果:
1 product.CategoryReference.EntityKey = new EntityKey("MyContainer.Categories", "ID", 2);這看起來(lái)有點(diǎn)復(fù)雜不是嗎?所以讓我們拆開(kāi)分析:
1. 對(duì)于每一個(gè)像Category一樣的引用"xxx",Entity Framework也生成了一個(gè)返回EntityReference類型的"xxxReference"屬性。(EF中大量使用EntityReference來(lái)提供完成像保持關(guān)系一致性,及基于EntityKeys的實(shí)體查找等操作所需的設(shè)施與服務(wù))
2. EntityReference的其中一個(gè)屬性是EntityKey。當(dāng)我們希望改變外鍵的值時(shí)我們需要將一個(gè)新的EntityKey設(shè)置到EntityKey屬性。
3. 要?jiǎng)?chuàng)建一個(gè)新的EntityKey,我們需要知道如下3條:
????1). 我們想要設(shè)置給"外鍵"的值。與前面代碼段一致,這個(gè)值就是2。
????2). 目標(biāo)Entity所屬的EntitySet的完成限定名。在這個(gè)例子中,我們的目標(biāo)是一個(gè)Category,我們由"MyContainer.Categories"集來(lái)獲取Category。
????3). 目標(biāo)實(shí)體中主鍵屬性的名稱。這個(gè)例子中Category的主鍵為"ID"屬性。
很顯然這不是你想要在你需要的任何地方書(shū)寫(xiě)的那種類型的代碼。所以我們以這種方式來(lái)封裝。以便…
怎樣使用這些代碼來(lái)仿造一個(gè)外鍵屬性?
幸運(yùn)的是Entity Framework為實(shí)體生成了部分類,所以你可以像這樣簡(jiǎn)單的將這些邏輯放置于你自己的Product部分類中:
1 public int CategoryID { 2 get {3 if (this.CategoryReference.EntityKey == null) return 0;4 else return (int) this.CategoryReference 5 .EntityKey.EntityKeyValues[0].Value; 6 } 7 set { 8 this.CategoryReference.EntityKey 9 = new EntityKey("MyContainer.Categories", "ID", value); 10 } 11 }注意我們?cè)趃et訪問(wèn)器中也使用了CategoryReference。這樣我們的CategoryID屬性僅就是一個(gè)基于CategoryReference的視圖,這樣如果EF在底層做了任何改變我們也無(wú)需被通知。
添加這樣的代碼后,之前我們想要的寫(xiě)法也就可以實(shí)現(xiàn):
1 product.CategoryID = 2;這在做法在很多場(chǎng)景中都特別有用,例如MVC控制器與數(shù)據(jù)綁定中。
外傳
這種解決方案本質(zhì)上僅僅是隱藏了獨(dú)立關(guān)聯(lián)的一些限制的變通方案,同所有的變通方案其也存在缺陷。關(guān)鍵就是在優(yōu)點(diǎn)與缺陷之間做出全面的權(quán)衡。一些關(guān)鍵的缺陷如下:
1. 部分類中的屬性不會(huì)被Entity Framework所識(shí)別,所以你不能在LINQ查詢中使用它們。
2. 由于在setter訪問(wèn)器中我們通過(guò)完全限定名引用了EntitySet,我們將Entity Class與EntitySet直接耦合在一起。對(duì)于大多數(shù)人這不成問(wèn)題,但是當(dāng)你試圖在不同上下文間重用你的實(shí)體類型或使用MEST(MultipleEntitySets per?Type)時(shí)這種方法不會(huì)工作。
3. 可能還有更多缺陷…當(dāng)想到它們時(shí)我將陸續(xù)添加!
?
提示8 – 怎樣使用LINQ to Entities編寫(xiě)"WHERE IN"形式的查詢
想象你有一個(gè)人員表,你想查詢其中姓氏包含于一個(gè)有趣的姓氏列表中的那部分人。在SQL中這很平常,你編寫(xiě)這樣的查詢:
1 SELECT * FROM People 2 WHERE Firstname IN ('Alex', 'Colin', 'Danny', 'Diego')SQL中的IN等價(jià)于LINQ中的Contains
在LINQ(to objects)的世界不存在'IN',所以你需要像倒置順序那樣來(lái)使用Contains方法:
1 var names = new string[] { "Alex", "Colin", "Danny", "Diego" }; 2 var matches = from person in people 3 where names.Contains(person.Firstname) 4 select person;注意語(yǔ)義上我們已經(jīng)由SQL中的:
?value.IN(set)???
變?yōu)長(zhǎng)INQ中的
?set.Contains(value)??
結(jié)果是相同的。
.NET 3.5 SP1與.NET 4.0對(duì)Contains的支持
在.NET 4.0的EF中將支持?IEnumerable<T>.Contains(T t)?方法,所以在下個(gè)版本EF中你可以編寫(xiě)上文LINQ查詢那樣的查詢。
不幸的是這種寫(xiě)法不被.NET 3.5 SP1支持:當(dāng)遇到這種形式的LINQ表達(dá)式時(shí)LINQ to Entities會(huì)報(bào)錯(cuò),因?yàn)槠洳恢涝鯓訉?duì)Contains的調(diào)用轉(zhuǎn)換為SQL。
但是我們.NET 3.5 SP1的用戶怎么辦呢?他們應(yīng)該怎樣做?
.NET 3.5 SP1的變通方案
有一個(gè)由EF團(tuán)隊(duì)"大腦"之一Colin提供的變通方案。
這種變通方案的本質(zhì)是你可以以如下方式來(lái)重寫(xiě)上面的查詢:
1 var matches = from person in people 2 where person.Firstname == "Alex" || 3 person.Firstname == "Colin" || 4 person.Firstname == "Danny" || 5 person.Firstname == "Diego" 6 select person;當(dāng)然這會(huì)使代碼變長(zhǎng)且編寫(xiě)這樣的代碼是痛苦的,但是它同樣可以工作。
所以如果有一些工具方法可以很容易的創(chuàng)建這種類型的LINQ表達(dá)式,我們將很好的處理業(yè)務(wù)。
而恰巧不久之前Colin在論壇上給出了他完成的幫助方法,借助他的方法,你可以如下這樣編寫(xiě)查詢:
1 var matches = ctx.People.Where( 2 BuildOrExpression<People, string>( 3 p => p.Firstname, names 4 ) 5 );這會(huì)生成一個(gè)與下面語(yǔ)句相同效果的查詢:
1 var matches = from p in ctx.People 2 where names.Contains(p.Firstname) 3 select p;但事實(shí)上后者一個(gè)明顯的問(wèn)題是其不可以在.NET 3.5 SP1下工作。
外傳
如果你已經(jīng)閱讀至此,很好!
下面是使變通寫(xiě)法成為可能的功能函數(shù):
1 public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>( 2 Expression<Func<TElement, TValue>> valueSelector, 3 IEnumerable<TValue> values 4 ) 5 { 6 if (null == valueSelector) 7 throw new ArgumentNullException("valueSelector");8 if (null == values) 9 throw new ArgumentNullException("values"); 10 ParameterExpression p = valueSelector.Parameters.Single(); 11 if (!values.Any()) 12 return e => false; 13 var equals = values.Select(value => 14 (Expression)Expression.Equal( 15 valueSelector.Body, 16 Expression.Constant( 17 value, 18 typeof(TValue) 19 ) 20 ) 21 ); 22 var body = equals.Aggregate<Expression>( 23 (accumulate, equal) => Expression.Or(accumulate, equal) 24 ); 25 26 return Expression.Lambda<Func<TElement, bool>>(body, p); 27 }此函數(shù)本質(zhì)上構(gòu)造了一個(gè)針對(duì)所有使用?valueSelector (i.e. p => p.Firstname)?的值斷言表達(dá)式,并將這些斷言進(jìn)行OR連接來(lái)為這個(gè)完整的預(yù)言創(chuàng)建一個(gè)表達(dá)式,除此之外,我不打算試圖解釋這個(gè)函數(shù)。
轉(zhuǎn)載于:https://www.cnblogs.com/sylone/p/6094548.html
總結(jié)
以上是生活随笔為你收集整理的Entity Framework技巧系列之二 - Tip 6 - 8的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 判断ic卡类型
- 下一篇: 在Asp.net core返回PushS