【问题标题】:When using Attach in EF, is it possible to not overwrite properties I haven't Updated?在 EF 中使用 Attach 时,是否可以不覆盖我尚未更新的属性?
【发布时间】:2013-07-28 09:27:33
【问题描述】:

我正在通过将现有实体附加到我的数据上下文来更新它,如下所示:

    var updatedDocumentState = new AccDocumentState()
    {
        Id = accDocumentState.Id,
        IsDocumentary = accDocumentState.IsDocumentary,
        IsEditable = accDocumentState.IsEditable,
        IsRecursive = accDocumentState.IsRecursive,
        Title = accDocumentState.Title,
       Reportable = accDocumentState.Reportable,

    };
        context.AccDocumentStates.Attach(updatedDocumentState);
        context.ObjectStateManager.ChangeObjectState(updatedDocumentState, System.Data.EntityState.Modified);
        flag = context.SaveChanges() > 0;

这可行,但是在保存附加实体后,我没有更新但我想保持原样的现有实体的属性被覆盖并赋予空值。如何附加我的实体并保留我尚未更新的现有实体的属性?

【问题讨论】:

  • 一个问题,您创建的实体是现有实体还是新实体(从数据库的角度来看)?
  • 它是一个现有的实体

标签: c# entity-framework entity-framework-5


【解决方案1】:

EF 有一个对象数据更改跟踪器。通过代理启用 Tracking changes in Poco entries

本质上是你/find 首先阅读 Object/Poco 实体。 仅更改您想要的那些属性。并保存。 仅更新更改的属性。

如果您没有使用 autoDetectChnages

 this.Configuration.AutoDetectChangesEnabled = false; ////<<<<<<<<< Default true

然后您将调用 Detect Changes before Saving。

但无论哪种方式,这个概念都是基于先读取以获取实体。 进行必要的更改并保存。

仅将实际更改发送回 Db。 例如:

  var mypoco = Context.Set<TPoco>.Find(1);
  myPoco.propertyXyz = "changed";
  // normally not required by default, But incase your are not using tracking proxies , tell ef heads Up
  // Context.Context.ChangeTracker.DetectChanges(); // uncomment when needed
  Context.SaveChanged();

仅将实际更改发送到 DB。

虽然来自 Rameez 的 POST 是正确的,但它并没有说明为什么将整个条目设置为已更改是可取的,也没有说明为什么要这样做?为什么要从文档链接状态条目?

   Context.Entry(poco).State = state;  // why do this ? or the objectContext equivalent 

这将导致所有值在 SaveChanges 上进入数据库的更新集 由于所有字段都将被视为已更改。这不是使用 EF 的好方法。

了解 EF 中的自动检测变化很重要。 见Automatic detect changesEntity states and SaveChanges

【讨论】:

    【解决方案2】:

    根据 msdn ,当您将实体对象条目的 EntityState 更改为已修改时,该对象的所有属性都将标记为已修改,无论当前值或原始值如何。 http://msdn.microsoft.com/en-us/library/system.data.objects.objectstatemanager.changeobjectstate.aspx

    因此,我认为所有其他属性都设置为 null,因为您创建的对象将其他属性设置为 null 或其默认值。 下面是修改后的代码。

     var updatedDocumentState = context.AccDocumentStates.First(a => a.Id== accDocumentState.Id);
                updatedDocumentState.IsDocumentary = accDocumentState.IsDocumentary,
                updatedDocumentState.IsEditable = accDocumentState.IsEditable,
                updatedDocumentState.IsRecursive = accDocumentState.IsRecursive,
                updatedDocumentState.Title = accDocumentState.Title,
                updatedDocumentState.Reportable = accDocumentState.Reportable,
                flag = context.SaveChanges() > 0;
    

    【讨论】:

    • 出于某种原因,我的上下文中没有Entry
    • 哦,好吧..我想我遇到了问题...您不能使用 EntityState ...因为从 MSDN 定义中说...当您将实体对象条目的 EntityState 更改为已修改时,无论当前值或原始值如何,对象的所有属性都被标记为已修改。 s
    【解决方案3】:

    作为解决您的问题的方法,仅为您正在更新的字段创建一个模型。假设这是一个常见的场景,并保证额外的模型避免额外调用 db。

    使用新的、最小化的模型,指向同一个表,但只有所需的属性,它可以按照您的意愿工作。当然,EF 端没有任何变化,但它只会更新它知道的属性。

    虽然我同意这不是 EF 的设计方式,但我也对执行更新或删除的额外数据库调用感到沮丧。该解决方案对此有所帮助。

    【讨论】:

    • 如果您只想修改某些字段,Tony 使用迷你模型是个好主意。它的范围有限,但可能有用。顺便说一句,删除问题实际上并不需要先阅读。您可以附加一个仅包含 Key 字段集的实体。然后请求删除。
    • @philsoady - 我只希望有一个 Context.Model.Delete(primaryKey);这会让生活更轻松。
    • 是的,为什么不写一个呢? Delete(params object[] keyValues) { 创建一个新的 Object 实例并设置关键字段。使用 ObjectContext.CreateEntityKey 或 ObjectContext.CreateObjectSet().EntitySet.ElementType.KeyMembers
    • 这是一个想法,也许可以做到!
    【解决方案4】:

    试试这个。也许可以按您的需要工作:

    var updatedDocumentState = context.AccDocumentStates.Find(accDocumentState.Id)
    {
        IsDocumentary = accDocumentState.IsDocumentary,
        IsEditable = accDocumentState.IsEditable,
        IsRecursive = accDocumentState.IsRecursive,
        Title = accDocumentState.Title,
        Reportable = accDocumentState.Reportable,
    };
    
    flag = context.SaveChanges() > 0;
    

    【讨论】:

      【解决方案5】:

      我在以下方面很幸运。首先,我创建了一个扩展方法来取消设置不在我想要限制更新的属性集中的任何属性的 IsModified 标志:

      public static void RestrictModifiedProps<ENT>(this DbContext context, ENT entity, IEnumerable<string> restrictedPropNames)
        where ENT : class
      {
      
        //Grab the meta entry that knows whether the entity/properties have been updated
        var entry = context.Entry(entity);
        if (entry == null) return;
      
        //loop over properties, only allow properties in the 
        //  restrictedPropNames list to be modified
        foreach (var propName in entry.CurrentValues.PropertyNames)
        {
          var prop = entry.Property(propName);
          if (!prop.IsModified) continue;
      
          prop.IsModified = restrictedPropNames.Any(O => O == propName);
        }
      }
      

      在我的例子中,我接受实体的属性值从 json 帖子到 MVC 操作。所以,我想找出发布了哪些属性并为控制器创建了一个(几个)扩展方法:

      public static JObject JsonPostData(this Controller cntrlr)
      {
        //ensure we're at the start of the input stream
        Stream req = cntrlr.Request.InputStream;
        req.Seek(0, SeekOrigin.Begin);
      
        //read in any potential json
        string json = d2s.SafeTrim(new StreamReader(req).ReadToEnd());
        if (string.IsNullOrWhiteSpace(json)
          || !json.StartsWith("{")
          || !json.EndsWith("}"))
          return null;
      
        //try to deserialize it
        return JsonConvert.DeserializeObject(json) as JObject;
      }
      
      public static IEnumerable<JProperty> JsonPostProperties(this Controller cntrlr)
      {
        JObject jObj = cntrlr.JsonPostData();
        if (jObj == null) return null;
      
      
        return jObj.Properties();
      }
      
      public static IEnumerable<string> JsonPostPropNames(this Controller cntrlr)
      {
        IEnumerable<JProperty> jProps = cntrlr.JsonPostProperties();
        if (jProps == null) return null;
      
        return jProps.Select(O => O.Name);
      }
      

      在动作中,我们得到:

      [HttpPost, ActionName("Edit")]
      public virtual ActionResult Edit_Post(ENT obj)
      {
      
        ...code...
      
        Ctxt.Set<ENT>().Attach(obj);
        Ctxt.Entry(obj).State = EntityState.Modified;
        Ctxt.RestrictModifiedProps(obj, this.JsonPostPropNames());
      
        ...code...
      
      }
      

      【讨论】:

        【解决方案6】:

        如果您只是排除一两个属性,例如说您从不想允许更新 Title 属性(在您的示例中),只需在设置对象状态之后取消设置目标属性上的 IsModified修改:

        context.AccDocumentStates.Attach(updatedDocumentState);
        context.ObjectStateManager.ChangeObjectState(updatedDocumentState, System.Data.EntityState.Modified);
        context.Entry(updatedDocumentState).Property("Title").IsModified = false;
        flag = context.SaveChanges() > 0;
        

        也仅供参考 - VS 中的默认 MVC5 项目使用此行来设置对象的修改属性:

        context.Entry(updatedDocumentState).State = System.Data.EntityState.Modified;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-11-05
          • 1970-01-01
          • 2012-02-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-10
          相关资源
          最近更新 更多