在EF中使用Attach时,是否可以不覆盖我拥有的属性';t已更新

本文关键字:拥有 覆盖 属性 已更新 EF Attach 是否 | 更新日期: 2023-09-27 18:25:24

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

    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;

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

在EF中使用Attach时,是否可以不覆盖我拥有的属性';t已更新

EF有一个对象数据更改跟踪器。通过代理启用跟踪Poco条目中的更改

本质上,你/发现先读取对象/Poco实体。只更改那些您想要的属性。并保存。只有更改后的属性才会更新。

如果您没有使用autoDetectChnages

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

然后您将调用保存前检测更改。

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

只有实际更改才会发送回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的自动检测变化是很重要的。请参阅自动检测更改和实体状态和SaveChanges

根据msdn将实体对象条目的EntityState更改为Modified时,无论当前值或原始值如何,对象的所有属性都将标记为Modifiedhttp://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;

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

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

虽然我同意EF不是这样设计的,但我也对进行更新或删除的额外DB调用感到沮丧。这个解决方案有助于实现这一点。

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

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;

我在以下方面运气不错。首先,我创建了一个扩展方法,为不在我想限制更新的属性集中的任何属性取消设置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...
}

如果你只是排除了一个或两个属性,比如说你从来都不想允许更新Title属性(在你的例子中),那么在将对象状态设置为modified:之后,只需在目标属性上取消设置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;