覆盖SaveChanges并设置ModifiedDate,但如何设置ModifiedBy?

nor*_*pro 23 c# ef-code-first entity-framework-4.1 asp.net-mvc-3

我有一个带有UI,业务(实体)和数据(DbContext)层的ASP.NET MVC3 Web应用程序.我正在使用Entity Framework 4.1 Code First.现在,我正在覆盖DbContext.SaveChanges()数据层,以便我可以设置对ModifiedDate实现我的IAuditable接口的任何实体对象所做的所有更改.我有一个DateProvider返回的静态类和方法(GetCurrentDate)DateTime.Now(除非我正在运行测试,在这种情况下,它返回我告诉它的任何内容).

我想自动将ModifiedBy属性设置为当前用户.这样做的最佳方法是什么?框架中是否有一些内容可以让我访问这些信息,或者我需要设置类似于类的东西DateProvider吗?这是一个Active Directory环境,我们WindowsAuthentication在IIS中使用.

这是我的SaveChanges代码:

public override int SaveChanges()
{
    var changeSet = ChangeTracker.Entries<IAuditable>();

    if (changeSet != null)
    {
        foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
        {
            entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
        }
    }
    return base.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

Era*_*nga 40

您可以使用它HttpContext.Current.User.Identity.Name来获取当前用户的名称.

public override int SaveChanges()
{
    var changeSet = ChangeTracker.Entries<IAuditable>();

    if (changeSet != null)
    {
        foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
        {
            entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
            entry.Entity.ModifiedBy = HttpContext.Current.User.Identity.Name;
        }
    }
    return base.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

更好的方法是使用构造函数注入将当前用户传递给上下文

public class MyContext : DbContext
{
    public MyContext(string userName)
    {
        UserName = userName;
    }

    public string UserName
    {
        get; private set;
    }

    public override int SaveChanges()
    {
       var changeSet = ChangeTracker.Entries<IAuditable>();

       if (changeSet != null)
       {
          foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
          {
              entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
              entry.Entity.ModifiedBy = UserName;
          }
       }
       return base.SaveChanges();
   }
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*hew 10

我还想在我的MVC 4/Entity Framework 5应用程序上自动化审计字段的数量.我使用来自@ Eranga的答案和本博客的信息:http://lourenco.co.za/blog/2013/07/audit-trails-concurrency-and-soft-deletion-with-entity-framework/ 使这种方法有效对我来说与Ninject一起发布 - 如果对任何其他人有价值的话:

创建了一个接口和抽象类:

public interface IAuditableEntity {
    DateTime? CreatedDate { get; set; }
    string CreatedBy { get; set; }
    DateTime? LastModifiedDate { get; set; }
    string LastModifiedBy { get; set; }
}

public abstract class AuditableEntity:IAuditableEntity {
    public DateTime? CreatedDate { get; set; }
    public string CreatedBy { get; set; }
    public DateTime? LastModifiedDate { get; set; }
    public string LastModifiedBy { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在我的实体中使用它们:

public class DataEntity : AuditableEntity {
    public int DataEntityID { get; set; }
    ...
}
Run Code Online (Sandbox Code Playgroud)

为MyDbContext添加了一个构造函数,它接受了HttpContext并覆盖了SaveChanges:

public EFDbContext(HttpContext context) {
    _context = context;
}

public HttpContext _context {
    get;
    private set;
}

public override int SaveChanges() {
    DateTime currentDateTime = DateTime.Now;

    foreach (var auditableEntity in ChangeTracker.Entries<IAuditableEntity>()) {
        if (auditableEntity.State == EntityState.Added || auditableEntity.State == EntityState.Modified) {
            auditableEntity.Entity.LastModifiedDate = currentDateTime;
            switch (auditableEntity.State) {
                    case EntityState.Added:
                        auditableEntity.Entity.CreatedDate = currentDateTime;
                        auditableEntity.Entity.CreatedBy = _context.User.Identity.Name;
                        break;
                    case EntityState.Modified:
                        auditableEntity.Entity.LastModifiedDate = currentDateTime;
                        auditableEntity.Entity.LastModifiedBy = _context.User.Identity.Name;
                        if (auditableEntity.Property(p => p.CreatedDate).IsModified || auditableEntity.Property(p => p.CreatedBy).IsModified) {
                            throw new DbEntityValidationException(string.Format("Attempt to change created audit trails on a modified {0}", auditableEntity.Entity.GetType().FullName));
                        }
                        break;
                }
            }
        }
        return base.SaveChanges();
    }
Run Code Online (Sandbox Code Playgroud)

最后 - 需要为每个请求提供一个DbContext并根据以下答案传递HttpContext,如下所示:https://stackoverflow.com/a/3617961/1803682 - 请注意,就像MyDbContext现在的请求范围一样,存储库也必须如此.

kernel.Bind<IDataRepository>()
      .To<EFDataRepository>()
      .InRequestScope();

kernel.Bind<MyDbContext>().ToSelf()
      .InRequestScope()
      .WithConstructorArgument("context", ninjectContext=>HttpContext.Current);
Run Code Online (Sandbox Code Playgroud)