C# 实体框架 DbContext SaveChanges() OriginalValue 不正确
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9588352/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Entity Framework DbContext SaveChanges() OriginalValue Incorrect
提问by Joe DePung
I am trying to implement an AuditLog using EF 4.1, by overriding the SaveChanges() method as discussed in the following places:
我正在尝试使用 EF 4.1 来实现 AuditLog,方法是覆盖 SaveChanges() 方法,如以下地方所述:
- http://jmdority.wordpress.com/2011/07/20/using-entity-framework-4-1-dbcontext-change-tracking-for-audit-logging/
- Entity Framework 4.1 DbContext Override SaveChanges to Audit Property Change
- http://jmdority.wordpress.com/2011/07/20/using-entity-framework-4-1-dbcontext-change-tracking-for-audit-logging/
- Entity Framework 4.1 DbContext 覆盖 SaveChanges 以审计属性更改
I am having problems with the "modified" entries though. Whenever I attempt to get at the OriginalValueof the property in question, it always has the same value as it does in the CurrentValuefield.
不过,我在“修改”条目方面遇到了问题。每当我尝试获取相关属性的OriginalValue时,它的值始终与CurrentValue字段中的值相同。
I first use this code, and it successfully identifies the Entries that are modified:
我首先使用此代码,它成功识别了被修改的条目:
public int SaveChanges(string userID)
{
// Have tried both with and without the following line, and received same results:
// ChangeTracker.DetectChanges();
foreach (
var ent in this.ChangeTracker
.Entries()
.Where( p => p.State == System.Data.EntityState.Added ||
p.State == System.Data.EntityState.Deleted ||
p.State == System.Data.EntityState.Modified ))
{
// For each change record, get the audit record entries and add them
foreach (AuditLog log in GetAuditRecordsForChange(ent, userID))
{
this.AuditLog.Add(log);
}
}
return base.SaveChanges();
}
The problem is in this (abbreviated code):
问题出在这个(缩写代码):
private List<AuditLog> GetAuditRecordsForChange(DbEntityEntry dbEntry, string userID)
{
if (dbEntry.State == System.Data.EntityState.Modified)
{
foreach (string propertyName in dbEntry.OriginalValues.PropertyNames)
{
if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName),
dbEntry.CurrentValues.GetValue<object>(propertyName)))
{
// It never makes it into this if block, even when
// the property has been updated.
}
// If I updated the property "Name" which was originally "OldName" to the value "NewName" and then break here and inspect the values by calling:
// ?dbEntry.OriginalValues.GetValue<object>("Name").ToString()
// the result will be "NewName" and not "OldName" as expected
}
}
}
The strange thing is that the call to dbEntry.Property(propertyName).IsModified();will
return true in this case. It is just that the OriginalValue doesn't have the expected value inside. Would anyone be willing to help point me in the right direction? I cannot seem to get this to work correctly.
奇怪的是,dbEntry.Property(propertyName).IsModified();在这种情况下,调用将返回 true。只是 OriginalValue 里面没有预期的值。有人愿意帮助我指出正确的方向吗?我似乎无法让它正常工作。
采纳答案by Arthur Vickers
When EF retrieves an entity from the database it takes a snapshot of the original values for all properties of that entity. Later, as changes are made to the values of these properties the original values will remain the same while the current values change.
当 EF 从数据库中检索实体时,它会为该实体的所有属性获取原始值的快照。稍后,随着对这些属性的值进行更改,原始值将保持不变,而当前值会发生变化。
However, for this to happen EF needs to be tracking the entity throughout the process. In a web or other n-tier application, typically the values are sent to the client and the context used to query the entity is disposed. This means that the entity is now no longer being tracked by EF. This is fine and good practice.
但是,要实现这一点,EF 需要在整个过程中跟踪实体。在 Web 或其他 n 层应用程序中,通常将值发送到客户端,并处理用于查询实体的上下文。这意味着实体现在不再被 EF 跟踪。这是很好的做法。
Once the application posts back the entity is reconstructed using values from the client and then re-attached to the context and set into a Modifiedstate. However, by default the only values that come back from the client are the current values. The original values are lost. Usually this doesn't matter unless you are doing optimistic concurrency or want to be very careful about only updating values that have really changed. In these cases the original values should also be sent to the client (usually as hidden fields in a web app) and then re-applied as the original values as a part of the attach process. This was not happening in the example above and this is why the original values were not showing as expected.
一旦应用程序回发,实体将使用来自客户端的值重建,然后重新附加到上下文并设置为修改状态。但是,默认情况下,从客户端返回的唯一值是当前值。原始值丢失。通常这无关紧要,除非您正在执行乐观并发或希望非常小心地仅更新已真正更改的值。在这些情况下,原始值也应该发送到客户端(通常作为 Web 应用程序中的隐藏字段),然后作为附加过程的一部分重新应用为原始值。这在上面的示例中没有发生,这就是原始值未按预期显示的原因。
回答by Mike Munro
If you change
如果你改变
dbEntry.OriginalValues.GetValue<object>(propertyName);
to
到
dbEntry.GetDatabaseValues().GetValue<object>(propertyName);
then that works.
那么就可以了。
回答by pejman
I got this error when i override SaveChanges in context As follows
当我在上下文中覆盖 SaveChanges 时出现此错误,如下所示
public override int SaveChanges()
{
var changeInfo = ChangeTracker.Entries()
.Select(t => new {
Original = t.OriginalValues.PropertyNames.ToDictionary(pn => pn, pn => t.OriginalValues[pn]),
Current = t.CurrentValues.PropertyNames.ToDictionary(pn => pn, pn => t.CurrentValues[pn]),
}).ToList();
return base.SaveChanges();
}
and when I cleared it fixed!
当我清除它固定!
ChangeTracker.Entries().ToList() in SaveChanges is wrong...
SaveChanges 中的 ChangeTracker.Entries().ToList() 是错误的...
回答by caras
You can get data that you haven't committed yet.
您可以获得尚未提交的数据。
var Current = _dbContext.Entry(entity).GetDatabaseValues().ToObject();
回答by user2555515
The problem is not in the code you show here. The issue is that how you track entities. If you just create an entity object and calls Update on it EF framework just overwrite the existing value in db ( provided you supplied correct ID ). That is done for efficiency. So if you do:
问题不在于您在此处显示的代码。问题在于您如何跟踪实体。如果您只是创建一个实体对象并对其调用 Update,EF 框架只需覆盖 db 中的现有值(前提是您提供了正确的 ID)。这样做是为了效率。所以如果你这样做:
var company = new Company{Id = mySuppliedId, Name = newname};
Context.Companies.Update(company);
Context.SaveChanges();
EF will go directly to DB in one shot and update all properties on the entity, without bringing anything back first. So it has no way of knowing the original values. If you change the code in your logic to something like:
EF 将一次性直接转到 DB 并更新实体上的所有属性,而不会先带回任何内容。所以它无法知道原始值。如果您将逻辑中的代码更改为:
var company = Context.Companies.Where(c=>c.Id == mySuppliedId).FirstOrDefault();
company.Name = newName;
Context.SaveChanges()
Then your ChangeTracker code you showed above all of sudden starts working, as EF brought the data from DB first. It is however less efficient as you make and extra query.
然后你上面显示的 ChangeTracker 代码突然开始工作,因为 EF 首先从 DB 带来数据。但是,当您进行额外查询时,它的效率会降低。

