用MVC实现审计日志/变更历史实体框架

本文关键字:历史 实体 框架 MVC 实现 审计 日志 | 更新日期: 2023-09-27 18:04:07

我正在构建使用实体框架的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);
    }
    ...
}

但是问题是当代码检索"未编辑的vm"时,这会导致EntityFramework中的一些意想不到的变化-因此TryUpdateModel(object);抛出UpdateException

所以问题是 -在这种情况下-我如何在EntityFramework之外创建object的副本来比较更改/审计历史,以便它不影响或更改EntityFramework

edit:不要使用触发器。需要记录用户名谁做的

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);
    }
}

用MVC实现审计日志/变更历史实体框架

如果您正在使用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,您可以通过本文提取更改

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

我知道您宁愿只看到一个代码片段来显示如何做到这一点,但是为了正确处理日志记录的所有情况,包括关系更改和多对多更改,代码会变得相当大。希望这个库能够很好地满足您的需求,但如果不是,您可以自由地修改代码。

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

在代码项目中有一篇评价很高的文章:使用实体框架实现审计跟踪。它似乎能做你想做的事。我已经开始在一个项目中使用这个解决方案。我首先在数据库中使用T-SQL编写触发器,但由于对象模型一直在发生变化,因此很难维护它们。