使用MVC和Entity Framework实现审计日志/更改历史记录

32

我正在为我的MVC应用程序建立一个变更历史/审计日志,该应用程序使用Entity Framework。

具体地,在编辑方法public ActionResult Edit(ViewModel vm)中,我们找到要更新的对象,然后使用TryUpdateModel(object)将表单上的值转换到要更新的对象上。

当该对象的任何字段更改时,我想记录更改历史。所以基本上我需要在编辑之前复制该对象,然后在TryUpdateModel(object)完成工作后进行比较。即:

[HttpPost]
public ActionResult Edit(ViewModel vm)
{
    //Need to take the copy here
    var object = EntityFramework.Object.Single(x=>x.ID = vm.ID);

    if (ModelState.IsValid)
    {
        //Form the un edited view model
        var uneditedVM = BuildViewModel(vm.ID); //this line seems to confuse the EntityFramework (BuildViewModel() is used to build the model when originally displaying the form)
        //Compare with old view model
        WriteChanges(uneditedVM, vm);
        ...
        TryUpdateModel(object);
    }
    ...
}

问题在于当代码获取“未编辑的vm”时,会导致实体框架出现一些意外的更改,从而使TryUpdateModel(object);抛出UpdateException异常。

因此问题是 - 在这种情况下 - 我该如何在不影响或更改实体框架的情况下创建一个object的副本以用于比较/审计历史记录?

编辑:不想使用触发器。需要记录执行操作的用户名。

编辑1:使用EFv4,不太确定如何覆盖SaveChanges(),但这可能是一种选项。


对于这样一个简单的需求,这条路似乎没有任何进展!最终我成功地进行了正确的覆盖,但是现在我用那段代码却得到了一个异常:

public partial class Entities
{
    public override int SaveChanges(SaveOptions options)
    {
        DetectChanges();
        var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
        foreach (var entry in modifiedEntities)
        {
            var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry).GetModifiedProperties(); //This line throws exception The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'System.Data.Objects.EntityEntry'.
            var currentValues = ObjectStateManager.GetObjectStateEntry(entry).CurrentValues;
            foreach (var propName in modifiedProps)
            {
                var newValue = currentValues[propName];
                //log changes
            }
        }

        //return base.SaveChanges();
        return base.SaveChanges(options);
    }
}

1
尝试这个:http://pastebin.com/puygwzyb - Omar
1
或者...你不需要自己创建!只需获取此NuGet包即可。http://www.nuget.org/packages/trackerenableddbcontext - Rosdi Kasim
@Rosdi Yeh,通过添加大量的膨胀代码来解决这个问题,这样做并不明智。实际上,只需要写几行代码就可以解决这个问题。我不明白为什么人们最近觉得需要为每件事情都引入包,而不是自己写几行代码。 - War
3个回答

24

如果您使用的是EF 4,则可以订阅SavingChanges事件。

由于Entities是一个部分类,因此您可以在单独的文件中添加其他功能。因此,请创建一个名为Entities的新文件,并在其中实现部分方法OnContextCreated以连接事件。

public partial class Entities
{
    partial void OnContextCreated()
    {
        SavingChanges += OnSavingChanges;
    }

    void OnSavingChanges(object sender, EventArgs e)
    {

        var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
        foreach (var entry in modifiedEntities)
        {
            var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties();
            var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues;
            foreach (var propName in modifiedProps)
            {
                var newValue = currentValues[propName];
                //log changes
            }
        }
    }
}

如果你正在使用EF 4.1,可以查看这篇文章来提取更改。


没有适合覆盖的方法。 - baron
@baron,你可以处理 SavingChanges 事件。 - Eranga
还有,“基类ObjectContext不包含无参构造函数”。你从哪个博客得到了这段代码? - baron
@baron 不需要创建一个扩展 ObjectContext 的单独的“Test”。相反,你需要在你的 Entities 构造函数中订阅。我已经编辑了我的回答。 - Eranga
@Eranga,请记住,实体是自动生成的,最好他创建一个继承自生成上下文的新类,而不是编辑生成代码。 - Omar
显示剩余2条评论

15
请看我为此目的编写的 Entity Framework 日志记录库 FrameLog。它是开源的,商业用途也可以使用。
我知道您更想看到一个代码片段来展示如何完成此过程,但要正确处理所有记录日志的情况,包括关系更改和多对多更改,代码会变得相当大。希望这个库能够满足您的需求,如果不行,您可以自由地修改代码。
FrameLog 可以记录所有标量属性和导航属性的更改,并允许您指定要记录的子集。

3
我在我的项目中实现了FrameLog,只花费了几个小时的集成工作,它已经可以很好地运行了。我现在正在将其用于我数据库中的单个表格,以便创建审计日志。 - Boggin
1
这是一个非常好的框架。非常易于使用和定制。在不到3小时的时间内,我们已经将其应用到了我们的项目中。谢谢。你做得很好,马丁 =) - fabriciorissetto
1
任何寻找简单审计日志的人,可以在这里找到:https://github.com/loresoft/EntityFramework.Extended。否则,FrameLog非常棒。 - flux
这里是另一个:Audit.EntityFramework - thepirat000

1
这里有一篇在codeproject上评分很高的文章:使用Entity Framework实现审计跟踪。它似乎可以满足你的需求。我已经开始在一个项目中使用这个解决方案了。我最初在数据库中用T-SQL编写触发器,但是随着对象模型不断变化,维护它们变得太困难了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接