2011-10-03 5 views
21

Possiedo un'applicazione Web ASP.NET MVC3 con livelli UI, Business (entità) e Dati (DbContext). Sto usando il codice di Entity Framework 4.1 First. In questo momento, sto sovrascrivendo lo DbContext.SaveChanges() nel livello dati in modo da poter impostare ModifiedDate per tutte le modifiche apportate a qualsiasi oggetto entità che implementa la mia interfaccia IAuditable. Ho una classe statica DateProvider e un metodo (GetCurrentDate) che restituisce DateTime.Now (a meno che non stia eseguendo un test, nel qual caso restituisce tutto ciò che gli ho detto).Sovrascrittura di SaveChanges e impostazione di ModifiedDate, ma come si imposta ModifiedBy?

Desidero impostare automaticamente la proprietà ModifiedBy anche all'utente corrente. Qual è il modo migliore per farlo? C'è qualcosa che è costruito nel framework che mi consentirà di accedere a queste informazioni o devo impostare qualcosa di simile alla classe DateProvider? Questo è un ambiente Active Directory e utilizziamo WindowsAuthentication in IIS.

Ecco il mio codice 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(); 
} 

risposta

37

È possibile utilizzare il HttpContext.Current.User.Identity.Name per ottenere il nome dell'utente corrente.

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

Meglio modo per farlo sarebbe quello di utilizzare l'iniezione costruttore per passare l'utente corrente al contesto

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

Mi piace molto il metodo di iniezione del costruttore. Grazie! – norepro

+0

Sì, ottima idea, l'iniezione del costruttore. – ProfK

10

Volevo anche per automatizzare la popolazione dei campi di audit sul mio MVC 4/Entity Framework 5 applicazioni. Ho usato le informazioni da @ risposta di Eranga e questo blog: http://lourenco.co.za/blog/2013/07/audit-trails-concurrency-and-soft-deletion-with-entity-framework/ per fare questo lavoro approccio per me con Ninject - distacco in caso di valore a chiunque altro:

creato un'interfaccia e classe astratta:

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

li ha usati nei miei entità:

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

Aggiunto un costruttore per MyDbContext che ha accettato la HttpContext e overrode 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(); 
    } 

Infine, è necessario disporre di un DbContext per richiesta e passare HttpContext come di seguito basato su questa risposta: https://stackoverflow.com/a/3617961/1803682 - nota che come MyDbContext è ora ambito di richiesta, anche i repository devono essere.

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

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

Ho dato un'occhiata alla risposta di @ Eranga e al suo blog: http://lourenco.co.za/blog/2013/07/audit-trails-concurrency-and-soft-deletion-with-entity-framework/. Per quanto vedo, salva le modifiche dei dati in una nuova tabella. Tuttavia, in questa tabella ci sono solo poche colonne e la tabella non mantiene i valori, cioè quale record è cambiato, qual è il vecchio valore o quale azione viene chiamata? Quindi, come ottenere questo? Per favore, se ti sbaglio, per favore, chiariscimi? Grazie in anticipo. –

+0

Risposta completa – lostmylogin