2011-10-03 68 views
21

我有一个带有UI,Business(实体)和Data(DbContext)图层的ASP.NET MVC3 Web应用程序。我使用的是实体框架4.1代码优先。现在,我重写了数据层中的DbContext.SaveChanges(),以便我可以为ModifiedDate设置对实现我的接口的任何实体对象所做的所有更改。我有一个静态的DateProvider类和方法(GetCurrentDate),返回DateTime.Now(除非我正在运行一个测试,在这种情况下,它会返回我告诉它的任何内容)。覆盖SaveChanges并设置ModifiedDate,但是如何设置ModifiedBy?

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

这里是我的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(); 
} 

回答

37

可以使用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(); 
} 

更好的办法做到这一点是使用构造器注入到当前用户传递到环境

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(); 
    } 
} 
+0

我真的很喜欢构造函数注入方法。谢谢! – norepro

+0

是的,好主意,构造函数注入。 – ProfK

10

我也想在我的MVC 4 /实体框架自动化审计领域的人口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; } 
} 

使用过在我的实体:

public class DataEntity : AuditableEntity { 
    public int DataEntityID { get; set; } 
    ... 
} 

增加了一个构造函数来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(); 
    } 

最后 - 需要每个请求都有一个DbContext,并根据此回答传递HttpContext,如下所示:https://stackoverflow.com/a/3617961/1803682 - 请注意,因为MyDbContext现在是Request范围,所以Repositories也必须如此。

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

kernel.Bind<MyDbContext>().ToSelf() 
     .InRequestScope() 
     .WithConstructorArgument("context", ninjectContext=>HttpContext.Current); 
+0

我看@ @ Eranga的答案和他的博客:http://lourenco.co.za/blog/2013/07/audit-trails-concurrency-and-soft-deletion-with-entity-framework/。据我所知,它将数据更改保存到新表中。但是,在这个表中只有少量的列,表中没有保存这些值,即哪个记录被改变,旧的值是多少,或者哪个动作被调用?那么,如何实现这一目标呢?如果我错了,你能否澄清我?提前致谢。 –

+0

完整答案 – lostmylogin