我正在尝试使用EF 4.1实现AuditLog,通过覆盖以下地方讨论的SaveChanges()方法:
我遇到了“修改过”的条目问题。每当我尝试使用相关属性的 OriginalValue 时,它总是与 CurrentValue 字段中的值相同。
我首先使用此代码,并成功识别已修改的条目:
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();
}
问题在于此(缩写代码):
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
}
}
}
奇怪的是,对dbEntry.Property(propertyName).IsModified();
的调用会
在这种情况下返回true。只是OriginalValue里面没有预期的值。有人愿意帮我指出正确的方向吗?我似乎无法使其正常工作。
答案 0 :(得分:13)
当EF从数据库中检索实体时,它会获取该实体的所有属性的原始值的快照。之后,当对这些属性的值进行更改时,原始值将保持不变,而当前值将更改。
然而,为了实现这一点,EF需要在整个过程中跟踪实体。在web或其他n层应用程序中,通常将值发送到客户端,并且处理用于查询实体的上下文。这意味着EF现在不再跟踪该实体。这是很好的做法。
应用程序回发后,使用客户端的值重建实体,然后重新附加到上下文并设置为 Modified 状态。但是,默认情况下,从客户端返回的唯一值是当前值。原始值丢失。通常这并不重要,除非您正在进行乐观并发或者只是想要更新已经真正改变的值。在这些情况下,原始值也应该发送到客户端(通常作为Web应用程序中的隐藏字段),然后作为附加过程的一部分重新应用为原始值。在上面的示例中没有发生这种情况,这就是原始值没有按预期显示的原因。
答案 1 :(得分:13)
如果你改变了
dbEntry.OriginalValues.GetValue<object>(propertyName);
到
dbEntry.GetDatabaseValues().GetValue<object>(propertyName);
那就行了。
答案 2 :(得分:0)
当我在上下文中覆盖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();
}
当我清除它时就解决了!
SaveChanges中的ChangeTracker.Entries()。ToList()错误...
答案 3 :(得分:0)
问题不在您在此处显示的代码中。问题是您如何跟踪实体。 如果您仅创建一个实体对象并对其调用EF框架上的Update,则只需覆盖db中的现有值(只要您提供了正确的ID即可)。这样做是为了提高效率。因此,如果您这样做:
var company = new Company{Id = mySuppliedId, Name = newname};
Context.Companies.Update(company);
Context.SaveChanges();
EF将一口气直接进入数据库并更新实体上的所有属性,而无需先返回任何内容。因此,它无法知道原始值。 如果您将逻辑代码更改为:
var company = Context.Companies.Where(c=>c.Id == mySuppliedId).FirstOrDefault();
company.Name = newName;
Context.SaveChanges()
然后,您首先显示的ChangeTracker代码突然开始工作,因为EF首先从数据库中获取了数据。但是,这样做的效率较低,并且需要额外的查询。
答案 4 :(得分:-1)
您可以获得尚未提交的数据。
var Current = _dbContext.Entry(entity).GetDatabaseValues().ToObject();