实体框架DbContext SaveChanges()OriginalValue不正确

时间:2012-03-06 17:03:09

标签: c# entity-framework entity-framework-4.1 ef-code-first

我正在尝试使用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里面没有预期的值。有人愿意帮我指出正确的方向吗?我似乎无法使其正常工作。

5 个答案:

答案 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();