2011-10-03 10 views
21

Tengo una aplicación web ASP.NET MVC3 con capas UI, Business (entidades) y Data (DbContext). Estoy utilizando Entity Framework 4.1 Code First. En este momento, estoy anulando el DbContext.SaveChanges() en la capa de datos para que pueda configurar el ModifiedDate para todos los cambios realizados a cualquier objeto de entidad que implemente mi interfaz IAuditable. Tengo una clase y método static DateProvider (GetCurrentDate) que devuelve DateTime.Now (a menos que esté ejecutando una prueba, en cuyo caso, devuelve lo que le dije).Reemplazando SaveChanges y estableciendo ModifiedDate, pero ¿cómo configuro ModifiedBy?

Me gustaría configurar automáticamente la propiedad ModifiedBy para el usuario actual también. ¿Cuál es la mejor manera de hacer esto? ¿Hay algo construido en el marco que me permita acceder a esta información o tengo que configurar algo así como la clase DateProvider? Este es un entorno de Active Directory y usamos WindowsAuthentication en IIS.

Aquí está mi código 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(); 
} 

Respuesta

37

Puede utilizar el HttpContext.Current.User.Identity.Name para obtener el nombre del usuario actual.

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

mejor manera de hacer esto sería el uso de la inyección de constructor para aprobar el usuario actual al contexto

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

Me gusta mucho el método de inyección del constructor. ¡Gracias! – norepro

+0

Sí, gran idea, la inyección del constructor. – ProfK

10

También quería automatizar la población de los campos de auditoría en mi/Entity Framework MVC 4 5 aplicación. He utilizado la información de @ respuesta de Eranga y este blog: http://lourenco.co.za/blog/2013/07/audit-trails-concurrency-and-soft-deletion-with-entity-framework/ para hacer este trabajo enfoque para mí con Ninject - Desplazamiento en el caso valioso para cualquier otra persona:

Creado una interfaz y clase abstracta:

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

los utilizó en mis entidades:

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

añadido un constructor para MyDbContext que aceptó las HttpContext y pasó por encima 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(); 
    } 

Finalmente, necesita tener un DbContext por solicitud y pasar HttpContext como se indica a continuación basándose en esta respuesta: https://stackoverflow.com/a/3617961/1803682 - tenga en cuenta que como MyDbContext es ahora alcance de solicitud, los repositorios también deben estarlo.

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

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

Echo un vistazo a la respuesta de @Eranga y su blog: http://lourenco.co.za/blog/2013/07/audit-trails-concurrency-and-soft-deletion-with-entity-framework/. Por lo que veo, guarda los cambios de datos en una nueva tabla. Sin embargo, en esta tabla solo hay algunas columnas y la tabla no conserva los valores, es decir, qué registro se cambia, ¿cuál es el valor anterior o qué acción se llama? Entonces, ¿cómo lograr esto? Si estoy equivocado, ¿podrías aclararme? Gracias por adelantado. –

+0

Respuesta completa – lostmylogin

Cuestiones relacionadas