在EF中使用Attach时,是否可以不覆盖我没有更新的属性?

时间:2013-07-28 09:27:34

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

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

    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;

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

6 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

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

基本上你/找到首先阅读对象/ Poco实体。 仅更改所需的属性。并保存。 仅更新已更改的属性。

如果您没有使用autoDetectChnages

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

然后你会在保存之前调用检测更改。

但无论哪种方式,这个概念都是基于Read first to get实体。 进行必要的更改并保存。

仅将实际更改发送回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是正确的,但它没有说明为什么将整个条目设置为已更改是可取的,也不是为什么这样做?为什么要从文档中链接State条目?

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

这将导致在SaveChanges上转到Database的所有值的UPdate Set 由于所有字段都将被视为已更改。这不是使用EF的好方法。

了解EF中的自动检测更改非常重要。 见Automatic detect changesEntity states and SaveChanges

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

试试这个。也许你需要的是:

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;

答案 4 :(得分: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...

}

答案 5 :(得分:0)

如果您只是要排除一个或两个属性,比如说您从不希望允许更新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;

另外FYI - VS中的默认MVC5项目使用此行来设置对象的修改属性:

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