Entity Framework 數據部分更新之Attach &&Detach


 

我們經常會遇到這樣的問題: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,我在網上找了一些辦法:

  1. 辦法一,直接寫成Sql 語句,從而更新數據庫中的對象
  2. 辦法二,通過id從數據庫中得到這個對象,然后Deatch這個對象

      對於辦法一,因為我們用的是EF,所以我們還想用EF的東西;對於辦法二,我們需要在寫一個方法,這個方法負責從數據庫來得到這個對象,有點麻煩,性能方面,沒有測試過,只是感覺麻煩。那么EF有么有給我們提供一些方法讓我們通過僅有的信息得到這個對象呢,答案是有。方法如下

            object originalItem = null;
            System.Data.EntityKey key = _context.CreateEntityKey("StoredAddresses", s);
            if (_context.TryGetObjectByKey(key, out originalItem))    
            {
                _context.Detach(originalItem);         
            }
注意:System.Data.EntityKey key = _context.CreateEntityKey("StoredAddresses", s);是EF5.0中的寫法
EF6.0中的寫法為System.Data.Entity.Core.EntityKey key = _context.CreateEntityKey("StoredAddresses", s);
這個方法放到這里有些難理解,我們可以將它放到我們的代碼中:
 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中是沒有的。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM