Entity Framework 4.1 DbContext使用記之一——如何查找實(shí)體? DbSet.Find函數(shù)的使用與實(shí)現(xiàn)
順便提一句,EF4.1的MSDN文檔已經(jīng)發(fā)布, http://msdn.microsoft.com/en-us/library/gg696172(v=VS.103).aspx。這一系列文章,可能需要您對EF有一些初級(jí)的認(rèn)識(shí)。頭一次寫這樣介紹性的文章,大家多提意見!
[4.13添加]EF4.1 RTW 版本也發(fā)布啦,http://www.microsoft.com/downloads/en/details.aspx?FamilyID=b41c728e-9b4f-4331-a1a8-537d16c6acdf&displaylang=en
第一篇為大家?guī)硇碌腁PI,DbSet<>.Find()。
過去我們常常用Where或First(FirstOrDefault)方法來查找對應(yīng)的實(shí)體,比如:
where p.Name.StartsWith("M")
select u;
var people = context.People.FirstOrDefault(p => p.Name == "Michael");
這樣查找的缺點(diǎn)是:即使我們相應(yīng)的實(shí)體已經(jīng)被ObjectContext緩存,EF還是會(huì)去執(zhí)行數(shù)據(jù)庫訪問,而數(shù)據(jù)庫訪問是被普遍認(rèn)為比較耗費(fèi)性能的。
EF4.1為我們提供了一個(gè)新的API: DbSet<>.Find()。它可以幫助我們通過主鍵來查找對應(yīng)的實(shí)體。并且如果相應(yīng)的實(shí)體已經(jīng)被ObjectContext緩存,EF會(huì)在緩存中直接返回對應(yīng)的實(shí)體,而不會(huì)執(zhí)行數(shù)據(jù)庫訪問。
比如我們查找主鍵PersonID為1的Person實(shí)體:
var person = context.People.Find(1);
也可用于聯(lián)合主鍵(比如查找主鍵PersonID=1, PersonName="Michael"的實(shí)體):
注意:此處輸入聯(lián)合主鍵的次序需要按照我們定義改實(shí)體類時(shí)聲明主鍵的次序。
和之前直接用Where或First的調(diào)用不同,F(xiàn)ind函數(shù)對于剛剛新增的實(shí)體也能找到:
{
context.People.Add(new People { PersonID = 100 });
var newPerson = context.People.Find(100);
}
了解了Find函數(shù)的基本用法后,讓我們來看看Find函數(shù)是如何實(shí)現(xiàn)的。這里我們借用了.NET Reflector來查看EF4.1的最重要的程序集EntityFramework.dll。
Find函數(shù)首先調(diào)用了EF的內(nèi)部實(shí)現(xiàn):
{
this.InternalContext.DetectChanges(false);
WrappedEntityKey key = new WrappedEntityKey(this.EntitySet, this.EntitySetName, keyValues, "keyValues");
object obj1 = this.FindInStateManager(key);
if (obj1 != null)
{
goto Label_003E;
}
object obj2 = this.FindInStore(key, "keyValues");
if ((obj2 != null) && !(obj2 is TEntity))
{
throw Error.DbSet_WrongEntityTypeFound(obj2.GetType().Name, typeof(TEntity).Name);
}
return (TEntity) obj2;
}
這里,EF首先調(diào)用了DetectChanges()來同步內(nèi)部ObjectContext跟蹤的對象的狀態(tài)。WrappedEntityKey則對過去的EntityKey做了一個(gè)包裝,每個(gè)主鍵對應(yīng)于一個(gè)KeyValuePair。將Find函數(shù)的輸入值一一包裝成KeyValuePair,并最終生成EntityKey對象。
之后,F(xiàn)ind函數(shù)調(diào)用了FindInStateManager函數(shù)。顧名思義,EF首先在內(nèi)部緩存中查找對應(yīng)實(shí)體。對于狀態(tài)是Unchanged、Modified以及Deleted的實(shí)體,F(xiàn)indInStateManager直接使用了過去ObjectContext.ObjectStateManager.TryGetObjectStateEntry方法,并且直接將EntityKey傳給該方法。對于狀態(tài)是Added的新增實(shí)體,這里需要做的處理稍微復(fù)雜一些。EF先調(diào)用了ObjectStateManager.GetObjectStateEntries(EntityState.Added)方法得到所有狀態(tài)是Added的Entry。然后依次比較主鍵的值(之前得到的KeyValuePair)以判定是否為所要的實(shí)體。在得到Added狀態(tài)的實(shí)體的值時(shí),EF也使用了ObjectStateEntry.CurrentValues。
如果在內(nèi)部緩存中找不到相應(yīng)的實(shí)體,F(xiàn)ind函數(shù)會(huì)調(diào)用FindInStore函數(shù)來對數(shù)據(jù)庫進(jìn)行查找。FindInStore函數(shù)內(nèi)部使用了Entity SQL的查詢,以這樣一個(gè)Entity SQL命令為基礎(chǔ):SELECT VALUE X FROM {0} AS X WHERE。再將對應(yīng)的主鍵名字拼接到這個(gè)命令中。主鍵值則以O(shè)bjectParameter的形式傳遞。最后FindInStore調(diào)用ObjectContext.CreateQuery<>(...).SingleOrDefault()函數(shù)來執(zhí)行Entity SQL查詢。由于最后使用了SingleOrDefault,如果數(shù)據(jù)庫中也沒有對應(yīng)的實(shí)體存在,F(xiàn)ind函數(shù)會(huì)返回NULL。
終于一口氣寫完,希望對您學(xué)習(xí)EF有所幫助吧!
系列博文之二: Entity Framework 4.1 DbContext使用記之二——DbSet.Local屬性的使用與實(shí)現(xiàn)。
系列博文之三: Entity Framework 4.1 DbContext使用記之三——如何玩轉(zhuǎn)實(shí)體的屬性值?
也歡迎到MSDN中文論壇ADO.NET與LINQ論壇來提問EF的問題啊,可以試試直接報(bào)我的名字Michael Sun,哈哈!
如需轉(zhuǎn)發(fā)請注明原文出處,謝謝: http://www.rzrgm.cn/LingzhiSun/archive/2011/03/22/EF41_Find.html


浙公網(wǎng)安備 33010602011771號(hào)