Entity Framework 4.1 DbContext使用記之二——如何玩轉(zhuǎn)本地實體? DbSet.Local屬性的使用與實現(xiàn)
說好為大家?guī)硪幌盗械奈恼拢F(xiàn)在就寫第二篇。開始之前,再啰嗦兩句,EF4.1 RTW版本已經(jīng)發(fā)布:http://www.microsoft.com/downloads/en/details.aspx?FamilyID=b41c728e-9b4f-4331-a1a8-537d16c6acdf&displaylang=en。第一篇有關(guān)DbSet.Find的文章,請看:http://www.rzrgm.cn/LingzhiSun/archive/2011/03/22/EF41_Find.html。
今天為大家?guī)?a rel="noopener nofollow">DbSet.Local屬性的使用與實現(xiàn)。和上次介紹的Find函數(shù)首先查找context中緩存的實體類似,DbSet的Local屬性也是返回context中緩存并且被跟蹤的實體。不同點在于,Local屬性不會返回狀態(tài)為EntityState.Deleted的實體,且即使緩存中什么實體都沒有,也不會對數(shù)據(jù)庫進(jìn)行訪問。這樣的設(shè)計也正符合Local(本地)之意。
看一個例子:
{
// 此處調(diào)用EF4.1的新擴展方法DbSet<>.Load()從數(shù)據(jù)庫導(dǎo)入對應(yīng)的實體到緩存中
db.People.Load();
db.People.Add(new Person { Name = "Michael" });
db.People.Remove(db.People.Find(1));
foreach (var p in db.People.Local)
{
// 這里調(diào)用了EF4.1的新方法Entry來得到實體的DbEntryEntry
Console.WriteLine("Found {0}: {1} with state {2}", p.PersonID, p.Name, db.Entry(p).State);
}
}
這里的輸出結(jié)果類似于:
Found 2: Jennie with state Unchanged
Found 3: Bob with state Unchanged
Found 4: Mike with state Unchanged
...
PersonID為1的實體不會出現(xiàn)在這里,因為此時它的狀態(tài)是Deleted。而姓名為Michael的實體因為是新增的實體,其主鍵還沒有在數(shù)據(jù)庫端生成,所以其PersonID為0。
Local屬性為什么這么設(shè)計呢?照理說狀態(tài)為Deleted的實體同樣應(yīng)該是本地緩存的啊!讀者可能會發(fā)現(xiàn),Local屬性返回的數(shù)據(jù)類型是ObservableCollection<T>。我們知道ObservableCollection<T>常被用于WPF數(shù)據(jù)綁定。其實Local屬性的設(shè)計也是為了方便大家做數(shù)據(jù)綁定的。試想在數(shù)據(jù)綁定時,如果被我們刪除的實體仍然出現(xiàn)在Local屬性的集合中,UI控件則會仍然顯示這些已被我們標(biāo)記為Deleted的實體。反之對于新增的實體(但還沒有將更新提交到數(shù)據(jù)庫),我們當(dāng)然希望在做數(shù)據(jù)綁定時,UI控件也能顯示它們的信息。再為大家介紹一些背景知識:過去我們?nèi)绻苯影芽丶壎ǖ紼F的ObjectSet<T>集合上,如果我們刪除某些實體(并未提交數(shù)據(jù)庫),UI控件會有相應(yīng)的反應(yīng)(也刪除實體)。但是當(dāng)我們調(diào)用類似AddObject方法在ObjectSet<T>里新增實體時,UI控件并不會有任何變化(新增實體不出現(xiàn)在控件)。個人覺得這是之前EF設(shè)計上的一些缺陷。有關(guān)更詳細(xì)的討論,請參閱這個MSDN帖子,http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/8ac0f9a9-00e0-431a-9e7a-cb31dde83828。
有關(guān)Local屬性的用法以及如何使用Local屬性來做WPF和Winform數(shù)據(jù)綁定,可以參閱這三篇ADO.NET產(chǎn)品組的博文:(如果大家需要,我也可以對這些博文做翻譯工作或者寫一些有關(guān)EF數(shù)據(jù)綁定的文章)
下面我們一起來分析下Local屬性的實現(xiàn)。
由于沒有EF4.1的源碼,所以仍然使用.NET Reflector。 DbSet<>.Local屬性是只讀的。其get方法調(diào)用EF內(nèi)部封裝的InternalSet<T>.Local屬性(同樣只讀)。這里和上次介紹的DbSet<>.Find函數(shù)一樣,先調(diào)用DetectChanges函數(shù)來同步POCO實體的狀態(tài)。(有關(guān)DetectChanges函數(shù),詳見MSDN文檔Working with POCO Entities )
{
get
{
this.InternalContext.DetectChanges(false);
if (this._localView == null)
{
DbLocalView<TEntity> view1 = this._localView;
}
return (this._localView = new DbLocalView<TEntity>(this.InternalContext));
}
}
這里判斷_localView對象是否為NULL,但這里的邏輯我也不是很明白。在咨詢了產(chǎn)品組之后發(fā)現(xiàn),這是Reflector 6.5的分析IL時的一個問題。正確的代碼應(yīng)該是使用了類似于C# ??的運算符來判斷_localView對象是否為NULL。如果為NULL則生成一個DbLocalView<TEntity>的對象并返回。
{
get
{
this.InternalContext.DetectChanges(false);
return this._localView ?? (this._localView = new DbLocalView<TEntity>(this.InternalContext));
}
}
下面看看DbLocalView<TEntity>類的構(gòu)造函數(shù):
{
this._internalContext = internalContext;
try
{
this._inStateManagerChanged = true;
foreach (TEntity local in this._internalContext.GetLocalEntities<TEntity>())
{
base.Add(local);
}
}
finally
{
this._inStateManagerChanged = false;
}
this._internalContext.RegisterObjectStateManagerChangedEvent(new CollectionChangeEventHandler(this.StateManagerChangedHandler));
}
這里兩個比較關(guān)鍵的函數(shù)是GetLocalEntities和RegisterObjectStateManagerChangedEvent。前者是調(diào)用了ObjectStateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Added | EntityState.Unchanged)以得到狀態(tài)為Modified、Added和Unchanged的實體對象。而后者則是Local屬性實現(xiàn)數(shù)據(jù)綁定功能的關(guān)鍵,將StateManagerChangedHandler這個事件處理函數(shù)賦給ObjectStateManager.ObjectStateManagerChanged事件。該事件是當(dāng)任何實體被添加或從ObjectStateManager中刪除時都會被激發(fā)的。
StateManagerChangedHandler函數(shù)其實就是在ObjectStateManagerChanged事件被激發(fā)時,調(diào)用對應(yīng)的ObservableCollection<T>.Remove或ObservableCollection<T>.Add操作。這樣也就將ObservableCollection<T>和EF緩存的實體聯(lián)系了起來,簡單的數(shù)據(jù)綁定便實現(xiàn)了。
{
try
{
this._inStateManagerChanged = true;
TEntity element = e.Element as TEntity;
if (element != null)
{
if ((e.Action == CollectionChangeAction.Remove) && base.Contains(element))
{
base.Remove(element);
}
else if ((e.Action == CollectionChangeAction.Add) && !base.Contains(element))
{
base.Add(element);
}
}
}
finally
{
this._inStateManagerChanged = false;
}
}
再次一口氣寫完。希望對您學(xué)習(xí)EF有所幫助吧!
系列博文之三: Entity Framework 4.1 DbContext使用記之三——如何玩轉(zhuǎn)實體的屬性值?
PS:同事開發(fā)了一個很cool的MSDN論壇桌面小工具,絕對給力!歡迎使用!(我也出了不少力啊
)
也歡迎到MSDN中文論壇ADO.NET與LINQ論壇來提問EF的問題啊,可以試試直接報我的名字Michael Sun,哈哈!
如需轉(zhuǎn)發(fā)請注明原文出處,謝謝: http://www.rzrgm.cn/LingzhiSun/archive/2011/03/25/EF41_Local.html


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