在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有一个对象数据更改跟踪器。通过代理启用跟踪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;