Entity Framework 数据部分更新之Attach/Detach(转)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Entity Framework 数据部分更新之Attach/Detach(转)相关的知识,希望对你有一定的参考价值。

我们经常会遇到这样的问题:Update一个entity的部分数据时,通常需要new一个新的对象,然后事这新的对象Attach到Context中,代码如下所示: 
 

 
/// <summary> 
/// 只更新storedAddress数据中的DefaultAddress字段,更新为false 
/// 将默认地址改为不是默认地址  
/// </summary> 
/// <param name="storedAddress">地址信息</param>  
public void Update(StoredAddress storedAddress)  
{ 
    StoredAddress s = new StoredAddress { StoredAddressID = storedAddress.StoredAddressID }; 
    s.DefaultAddress = true;  
    _context.StoredAddresses.Attach(s); 
    s.DefaultAddress = false; 
    _context.SaveChanges(); 
    _context.Detach(s);
} 

  _context.StoredAddresses.Attach(s);程序在这一句时往往会报出异常---Context 中已经存在有相同键的对象了,从而使得我们的部分更新不能成功。 

经过分析,我们知道Context 中存在了一个对象,这个对象和我们new的对象s有相同的键,那么这个对象哪一个对象呢?通过代码我们不难看出这个对象是storedAddress,所以我们需要将storedAddress对象从Context 中Detach。我们的新代码如下:    

/// <summary> 
/// 只更新storedAddress数据中的DefaultAddress字段,更新为false         /// 将默认地址改为不是默认地址  
/// </summary> 
/// <param name="storedAddress">地址信息</param> 
public void UpdateStoredAddressDefaultAddress(StoredAddress storedAddress)
{ 
    //先撤销跟踪 
     _context.Detach(storedAddress);   
    StoredAddress s = new StoredAddress { StoredAddressID = storedAddress.StoredAddressID }; 

    s.DefaultAddress = true;   

    _context.StoredAddresses.Attach(s); 
    s.DefaultAddress = false;  
    _context.SaveChanges(); 
    _context.Detach(s);
}  

通常我们并不知道对象有没有Attach,下面提供一个方法来确定对象有没有Attach:  

 public static bool IsAttached(this AllureContext context, object entity)  
{ 
    if (entity == null) 
    { 
        throw new ArgumentNullException("entity"); 
    } 
    ObjectStateEntry entry; 
    if (context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry)) 
    { 
        return (entry.State != System.Data.EntityState.Detached); 
    } 
        return false;          
}   

上面的方法调用为_context.IsAttached(storedAddress),这要求我们必须知道一个对象,然后才能判断这个对象有没有被Attached。这是比较普遍的解决办法,因为我们的storedAddress对象已经确定。如果我们将第一段代码改为:   

/// 只更新storedAddress数据中的DefaultAddress字段,更新为false
/// 将默认地址改为不是默认地址
/// </summary> 
/// <param name="id">需要修改的对象的id</param> 
public void Update(int id)
{                
     StoredAddress s = new StoredAddress { StoredAddressID = id };   

    s.DefaultAddress = true;   
    _context.StoredAddresses.Attach(s); 

    s.DefaultAddress = false;   
    _context.SaveChanges(); 
    _context.Detach(s); 
} 

此时我们不知道有没有和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);                      
} 

这个方法放到这里有些难理解,我们可以将它放到我们的代码中: 

 

/// 只更新storedAddress数据中的DefaultAddress字段,更新为false  
/// 将默认地址改为不是默认地址 
/// </summary> 
/// <param name="id">需要修改的对象的id</param>  
public void Update(int id) 
{ 
    StoredAddress s = new StoredAddress { StoredAddressID = id };  
    object originalItem = null; 
    System.Data.EntityKey key = _context.CreateEntityKey("StoredAddresses", s); 
    if (_context.TryGetObjectByKey(key, out originalItem))
    { 
        _context.Detach(originalItem); 
    }  

    s.DefaultAddress = true;  
    _context.StoredAddresses.Attach(s);             
    s.DefaultAddress = false;              
    _context.SaveChanges(); 
    _context.Detach(s);              
} 

我们通过EntityKey来获得这个对象,然后Detach这个对象,随后再Attach我们new的对象s,就可以修改数据库了。此方法中要注意CreateEntityKey()的用法,第一个参数entitySetName,注意名称要和 Navigation Properties的名称一样,具体详见

http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.createentitykey.aspx   
好了,我们的修改已经完成。

以上是关于Entity Framework 数据部分更新之Attach/Detach(转)的主要内容,如果未能解决你的问题,请参考以下文章

UWP开发之ORM实践:如何使用Entity Framework Core做SQLite数据持久层?

在服务器中使用 Entity Framework 的 Migration 更新数据库

entity framework怎么查询部分数据

《Entity Framework 6 Recipes》中文翻译系列 (12) -----第三章 查询之使用SQL语句 (转)

Code First Entity Framework 6化被动为主动之explicit loading模式实战分析( 附源码)

Entity Framework技巧系列之十二 - Tip 46 - 50