通过简单注入器拦截/装饰实体框架的DbContext.SaveChanges()

时间:2014-11-14 15:06:01

标签: c# entity-framework simple-injector

我正在根据Entity Framework中Context对象中跟踪的更改编写AuditLog。

我需要能够拦截对Context.SaveChanges()的调用,然后我遍历Context.ChangeTracker.Entries()并将这些调用记录到我的AuditLog。

我需要能够在DbContext本身上执行此操作,因为我可以直接通过我的UnitOfWork直接获取DbContext和PerWebRequest。

我试图通过装饰器解决这个问题,这对我不起作用!

public class AuditLogSaveChangesInKNContextDecorator : DbContext
{
    DbContext _context;
    IHandleCommand<AddAuditLogEntriesFromTrackedChangesAndSaveChangesCommand> _handler;

    public AuditLogSaveChangesInKNContextDecorator(DbContext context,
        IHandleCommand<AddAuditLogEntriesFromTrackedChangesAndSaveChangesCommand> handler)
    {
        _context = context;
        _handler = handler;
    }

    public override int SaveChanges()
    {
        var changes = base.SaveChanges();

        _handler.Handle(new AddAuditLogEntriesFromTrackedChangesAndSaveChangesCommand { 
            Context = _context 
        });

        return changes;
    }
}

任何想法?

2 个答案:

答案 0 :(得分:9)

你在做什么对我来说似乎很对。拦截&#39;的唯一方法对SaveChanges的调用是通过覆盖该方法;它是虚拟的。例如:

public class MyDbContext : DbContext
{
    public event Action<MyDbContext> SavingChanges = _ => { };

    public override int SaveChanges()
    {
        // Notify objects that want to know, that we are gonna save some stuff
        this.SavingChanges(this);

        // Call the actual SaveChanges method to apply the changes to the database
        return base.SaveChanges();
    }
}

通过使用事件,我们可以向MyDbContext添加任何行为(依赖注入),而不必知道该上下文。例如:

container.RegisterPerWebRequest<MyDbContext>(() =>
{
    var context = new MyDbContext();
    context.SavingChanges += UpdateEntities;
    return context;
});

private static void UpdateEntities(MyDbContext db)
{
    var addedEntities =
        from entry in db.ChangeTracker.Entries()
        where entry.State == EntityState.Added
        select entry.Entity as IEntity;

    db.AuditTrailEntries.AddRange(
        from entity in addedEntities
        select new AuditTrailEntry 
        { 
            EntityId = entity.Id, 
            Type = entity.GetType().Name 
        });
}

答案 1 :(得分:1)

您在错误的SaveChanges()上致电DbContext

而不是:

var changes = base.SaveChanges();

您应该使用:

var changes = _context.SaveChanges();

由于_context是您要跟踪的DbContext,因此您需要在该项目上进行保存,而不是在装饰器对象上进行保存。