zoukankan      html  css  js  c++  java
  • 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中是没有的。

  • 相关阅读:
    红线行动开发文档
    团队作业1
    第二次作业:安装VS2015和使用自动测试管理工具
    简单介绍VS2015自动测试工具
    软件工程作业(一)
    三带一队 实验十 团队作业6:团队项目用户验收&BETA冲刺
    《三带一队》【Beta】Scrum meeting 4
    《三带一队》【Beta】Scrum meeting 3
    《三带一队》【Beta】Scrum meeting 2
    《三带一队》【Beta】Scrum meeting 1
  • 原文地址:https://www.cnblogs.com/zjf1987/p/3227239.html
Copyright © 2011-2022 走看看