我們經常會遇到這樣的問題:Update一個entity的部分數據時,通常需要new一個新的對象,然后事這新的對象Attach到Context中,代碼如下所示:
1 /// <summary> 2 /// 只更新storedAddress數據中的DefaultAddress字段,更新為false 3 /// 將默認地址改為不是默認地址 4 /// </summary> 5 /// <param name="storedAddress">地址信息</param> 6 public void Update(StoredAddress storedAddress) 7 { 8 StoredAddress s = new StoredAddress { StoredAddressID = storedAddress.StoredAddressID }; 9 s.DefaultAddress = true; 10 11 _context.StoredAddresses.Attach(s); 12 13 s.DefaultAddress = false; 14 15 _context.SaveChanges(); 16 _context.Detach(s); 17 }
_context.StoredAddresses.Attach(s);程序在這一句時往往會報出異常---Context 中已經存在有相同鍵的對象了,從而使得我們的部分更新不能成功。
經過分析,我們知道Context 中存在了一個對象,這個對象和我們new的對象s有相同的鍵,那么這個對象哪一個對象呢?通過代碼我們不難看出這個對象是storedAddress,所以我們需要將storedAddress對象從Context 中Detach。我們的新代碼如下:
1 /// <summary> 2 /// 只更新storedAddress數據中的DefaultAddress字段,更新為false 3 /// 將默認地址改為不是默認地址 4 /// </summary> 5 /// <param name="storedAddress">地址信息</param> 6 public void UpdateStoredAddressDefaultAddress(StoredAddress storedAddress) 7 { 8 //先撤銷跟蹤 9 _context.Detach(storedAddress); 10 11 StoredAddress s = new StoredAddress { StoredAddressID = storedAddress.StoredAddressID }; 12 s.DefaultAddress = true; 13 14 _context.StoredAddresses.Attach(s); 15 16 s.DefaultAddress = false; 17 18 _context.SaveChanges(); 19 _context.Detach(s); 20 }
通常我們並不知道對象有沒有Attach,下面提供一個方法來確定對象有沒有Attach:
1 public static bool IsAttached(this AllureContext context, object entity) 2 { 3 if (entity == null) 4 { 5 throw new ArgumentNullException("entity"); 6 } 7 ObjectStateEntry entry; 8 if (context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry)) 9 { 10 return (entry.State != System.Data.EntityState.Detached); 11 } 12 return false; 13 }
上面的方法調用為_context.IsAttached(storedAddress),這要求我們必須知道一個對象,然后才能判斷這個對象有沒有被Attached。這是比較普遍的解決辦法,因為我們的storedAddress對象已經確定。如果我們將第一段代碼改為:
1 /// 只更新storedAddress數據中的DefaultAddress字段,更新為false 2 /// 將默認地址改為不是默認地址 3 /// </summary> 4 /// <param name="id">需要修改的對象的id</param> 5 public void Update(int id) 6 { 7 8 StoredAddress s = new StoredAddress { StoredAddressID = id }; 9 s.DefaultAddress = true; 10 11 _context.StoredAddresses.Attach(s); 12 13 s.DefaultAddress = false; 14 15 _context.SaveChanges(); 16 _context.Detach(s); 17 }
此時我們不知道有沒有和s對象具有相同鍵的對象存在於Context,就算我們現在知道有個對象存在Context中,並且和s對象就有相同的鍵,但是我們不知到這個對象是什么,想要Detach這個對象,比較難了,因為只有一個id,我在網上找了一些辦法:
- 辦法一,直接寫成Sql 語句,從而更新數據庫中的對象
- 辦法二,通過id從數據庫中得到這個對象,然后Deatch這個對象
對於辦法一,因為我們用的是EF,所以我們還想用EF的東西;對於辦法二,我們需要在寫一個方法,這個方法負責從數據庫來得到這個對象,有點麻煩,性能方面,沒有測試過,只是感覺麻煩。那么EF有么有給我們提供一些方法讓我們通過僅有的信息得到這個對象呢,答案是有。方法如下
System.Data.EntityKey key = _context.CreateEntityKey("StoredAddresses", s);
if (_context.TryGetObjectByKey(key, out originalItem))
_context.Detach(originalItem);
1 /// 只更新storedAddress數據中的DefaultAddress字段,更新為false 2 /// 將默認地址改為不是默認地址 3 /// </summary> 4 /// <param name="id">需要修改的對象的id</param> 5 public void Update(int id) 6 { 7 8 StoredAddress s = new StoredAddress { StoredAddressID = id }; 9 10 object originalItem = null; 11 System.Data.EntityKey key = _context.CreateEntityKey("StoredAddresses", s); 12 if (_context.TryGetObjectByKey(key, out originalItem)) 13 { 14 _context.Detach(originalItem); 15 } 16 17 s.DefaultAddress = true; 18 19 _context.StoredAddresses.Attach(s); 20 21 s.DefaultAddress = false; 22 23 _context.SaveChanges(); 24 _context.Detach(s); 25 }
我們通過EntityKey來獲得這個對象,然后Detach這個對象,隨后再Attach我們new的對象s,就可以修改數據庫了。此方法中要注意CreateEntityKey()的用法,第一個參數entitySetName,注意名稱要和 Navigation Properties的名稱一樣,具體詳見http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.createentitykey.aspx
好了,我們的修改已經完成。
補充一下,我們的DBcontext實現為:
public partial class Context : ObjectContext
{
//具體實現
}
而不是DbContext。
這是兩者最根本的區別:ObjectContext是一種模型優先的開發模式,DbContext是代碼優先的開發模式。
所以有些方法是不同的,比如_context.Entry()方法在ObjectContext中是沒有的。