使用MVC和&amp ;;实现审计日志/更改历史记录实体框架

时间:2011-07-29 00:44:30

标签: c# asp.net-mvc entity-framework audit

我正在使用实体框架将更改历史记录/审核日志构建到我的MVC应用程序。

特别是在编辑方法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);
    }
    ...
}

但问题是当代码检索“未编辑的虚拟机”时,这会导致实体框架中出现一些意外更改 - 以便TryUpdateModel(object);抛出UpdateException

所以问题是 - 在这种情况下 - 如何在EntityFramework之外创建object的副本来比较更改/审核历史记录,以便它不会影响或改变了 所有的实体框架

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

edit1:使用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);
    }
}

3 个答案:

答案 0 :(得分:23)

如果您使用的是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,则可以通过this article提取更改

答案 1 :(得分:15)

请参阅FrameLog,我为此目的编写的实体框架日志库。它是开源的,包括商业用途。

我知道您只想看一个显示如何执行此操作的代码段,但为了正确处理日志记录的所有情况,包括关系更改和多对多更改,代码会变得非常大。希望图书馆能够很好地满足您的需求,但如果没有,您可以自由地调整代码。

FrameLog可以记录对所有标量和导航属性的更改,还允许您指定您有兴趣记录的子集。

答案 2 :(得分:1)

在codeproject上有一篇评分很高的文章:Implementing Audit Trail using Entity Framework。它似乎做你想要的。我已经开始在项目中使用此解决方案。我首先在数据库中用T-SQL编写了触发器,但是随着对象模型的变化一直发生而难以维护它们。