2012-03-06 55 views
6

我试图实现使用EF 4.1的审计日志,通过如在以下地方讨论重写的SaveChanges()方法:实体框架的DbContext的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

你是如何查询你的实体,然后改变属性值?如果你基本上是附加实体,然后将状态设置为修改,那么原始值将会丢失。要保留原始值,您需要让EF一直追踪实体直到保存,否则您需要跟踪自己代码中的原始值。 – 2012-03-06 18:11:27

+0

对不起 - 我试图在评论中发布一些代码,但它运行不正常。我正在使用MVC控制器的[HttpPost]动作。这会调用我的产品存储库的“SaveProduct”方法。在存储库中,它看起来确实调用了'context.Entry(product).State = EntityState.Modified',然后我在上下文中调用SaveChanges。 你能指点我的任何资源来展示你提到的两种技术吗?或者至少给我几个指点? – 2012-03-06 18:25:40

+0

我在MVC中建议使用隐藏字段。基本上你会将你关心的原始值保存到隐藏的字段中,然后在POST中将它们读回来。我不知道这方面的最佳做法,或者是否有MVC抽象帮助。 – 2012-03-06 18:40:32

回答

11

当EF从数据库中检索实体时,它会为该实体的所有属性获取原始值的快照。之后,随着对这些属性值的更改,原始值将保持不变,而当前值会更改。

但是,要发生这种情况,EF需要在整个过程中跟踪实体。在Web或其他n层应用程序中,通常将值发送到客户端,并且处理用于查询实体的上下文。这意味着该实体现在不再被EF跟踪。这是很好的做法。

一旦应用程序回传实体使用来自客户端的值重建,然后重新附加到上下文并设置为修改状态。但是,默认情况下,客户端返回的唯一值是当前值。原始值丢失。通常情况下,这并不重要,除非您正在进行乐观并发或者只想更新真正更改的值。在这些情况下,原始值也应该发送给客户端(通常作为Web应用程序中的隐藏字段),然后作为附加过程的一部分重新应用为原始值。这在上面的例子中没有发生,这就是为什么原始值没有按预期显示。

+0

再次感谢您的帮助。我已经按预期工作了......我想。只是澄清:你是说调用** context.Entry(product).State = EntityState.Modified **重新附加'产品'的上下文,因此它失去了它的OriginalValues跟踪?此外,[这里](http://stackoverflow.com/questions/9591165/ef-4-how-to-properly-update-object-in-dbcontext-using-mvc-with-repository-patte)是新帖子我为我的跟进做了准备(仅供其他人参考)。 – 2012-03-06 21:15:20

+0

但是为什么该方法在@JoeDePung问题中链接到更改跟踪示例中? – Jensen 2015-11-23 12:36:27

13

如果更改

dbEntry.OriginalValues.GetValue<object>(propertyName); 

dbEntry.GetDatabaseValues().GetValue<object>(propertyName); 

那么有效。

+4

工程,但审计目的可能非常昂贵。 – eoleary 2013-01-31 22:28:42

+1

为什么非常膨胀?如果你想知道更改前后的值,你必须查询数据库,没办法。 – sintetico82 2015-10-08 10:44:05

+0

它的工作原理..但是,第一个不保留原始值的原因是什么以及为什么与当前值相同@eoleary – Abi 2017-05-31 08:46:56

相关问题