2013-05-15 16 views
10

Dopo aver letto una domanda nel collegamento allegato, ho avuto un'idea di come impostare le colonne DateCreated e DateModified in Entity Framework e utilizzarlo nella mia applicazione. Nel vecchio modo SQL, tuttavia, il modo di trigger è più popolare perché è più sicuro dal punto di vista di DBA.Colonna DataCreated o Modified - Entity Framework o utilizzo di trigger su SQL Server

Quindi qualsiasi consiglio su quale via è la migliore pratica? dovrebbe essere impostato nel framework dell'entità ai fini dell'integrità dell'applicazione? o dovrebbe usare il trigger come ha più senso dal punto di vista della sicurezza dei dati? O c'è un modo per comporre il trigger nel framework delle entità? Grazie.

EF CodeFirst: Rails-style created and modified columns

BTW, anche se non importa molto, sto costruendo questo prodotto utilizzando ASP.NET MVC C#.

risposta

20

Opinione: I trigger sono come un comportamento nascosto, a meno che non li cerchi, di solito non ti rendi conto che sono lì. Mi piace anche mantenere il DB più "stupido" possibile quando si utilizza EF, dal momento che sto usando EF quindi il mio team non avrà bisogno di mantenere il codice SQL.

Per la mia soluzione (mix di ASP.NET WebForms e MVC in C# con Business Logic in un altro progetto, che contiene anche il DataContext):

Recentemente ho avuto un problema simile, e anche se per la mia situazione è era più complesso (DatabaseFirst, quindi richiedeva un file TT personalizzato), la soluzione è quasi sempre la stessa.

ho creato un'interfaccia:

public interface ITrackableEntity 
{ 
    DateTime CreatedDateTime { get; set; } 
    int CreatedUserID { get; set; } 
    DateTime ModifiedDateTime { get; set; } 
    int ModifiedUserID { get; set; } 
} 

Poi ho appena implementato tale interfaccia su tutte le entità che avevo bisogno di (perché la mia soluzione era DatabaseFirst, ho aggiornato il file TT per verificare se il tavolo aveva quei quattro colonne, e se è così ha aggiunto l'interfaccia all'output).

UPDATE: ecco la mia modifiche al file TT, dove ho aggiornato il metodo EntityClassOpening():

public string EntityClassOpening(EntityType entity) 
{ 
    var trackableEntityPropNames = new string[] { "CreatedUserID", "CreatedDateTime", "ModifiedUserID", "ModifiedDateTime" }; 
    var propNames = entity.Properties.Select(p => p.Name); 
    var isTrackable = trackableEntityPropNames.All(s => propNames.Contains(s)); 
    var inherits = new List<string>(); 
    if (!String.IsNullOrEmpty(_typeMapper.GetTypeName(entity.BaseType))) 
    { 
     inherits.Add(_typeMapper.GetTypeName(entity.BaseType)); 
    } 
    if (isTrackable) 
    { 
     inherits.Add("ITrackableEntity"); 
    } 

    return string.Format(
     CultureInfo.InvariantCulture, 
     "{0} {1}partial class {2}{3}", 
     Accessibility.ForType(entity), 
     _code.SpaceAfter(_code.AbstractOption(entity)), 
     _code.Escape(entity), 
     _code.StringBefore(" : ", String.Join(", ", inherits))); 
} 

L'unica cosa rimasta era quella di aggiungere il seguente alla mia parziale classe DataContext:

public override int SaveChanges() 
    { 
     // fix trackable entities 
     var trackables = ChangeTracker.Entries<ITrackableEntity>(); 

     if (trackables != null) 
     { 
      // added 
      foreach (var item in trackables.Where(t => t.State == EntityState.Added)) 
      { 
       item.Entity.CreatedDateTime = System.DateTime.Now; 
       item.Entity.CreatedUserID = _userID; 
       item.Entity.ModifiedDateTime = System.DateTime.Now; 
       item.Entity.ModifiedUserID = _userID; 
      } 
      // modified 
      foreach (var item in trackables.Where(t => t.State == EntityState.Modified)) 
      { 
       item.Entity.ModifiedDateTime = System.DateTime.Now; 
       item.Entity.ModifiedUserID = _userID; 
      } 
     } 

     return base.SaveChanges(); 
    } 

Nota che ho salvato l'ID utente corrente in un campo privato della classe DataContext ogni volta che l'ho creato.

+0

questa è una risposta ben dettagliata, grazie Tim. Sono convinto a modo tuo ed è bene mantenere questa logica nel livello dell'applicazione. Pensi che sia possibile o utile per il team EF aggiungere vincoli predefiniti e attivare il supporto in DataAnnotation o FluentAPI? – anIBMer

+1

Vorrei dare il benvenuto al team EF aggiungendo che renderebbe le cose più semplici con regole semplici. Detto questo, ci sono così spesso eccezioni alle regole, quindi sapere come prendere il pieno controllo del processo è un'abilità preziosa da apprendere. –

+0

Esattamente quali aggiornamenti hai apportato al file TT? – Fergal

6

Quanto DateCreated, mi sarebbe solo aggiungere un vincolo predefinito su quella colonna insieme al SYSDATETIME() che ha effetto quando si inserisce una nuova riga nella tabella.

Per DateModified, personalmente, probabilmente utilizzerei i trigger su quei tavoli.

A mio parere, l'approccio di innesco:

  • rende più facile; Non devo preoccupare di e ricordare ogni volta che li risparmio un'entità per impostare che DateModified

  • rende "più sicuro", in quanto si applicherà anche la DateModified se qualcuno trova un modo per aggirare la mia applicazione di modificare i dati in il database direttamente (usando ad esempio Access o Excel o qualcosa del genere).

+0

Sto utilizzando il codice per primo, quindi è possibile creare un trigger in DB-Migration. Sebbene aggiungere un vincolo predefinito a una colonna, non penso di poterlo fare tramite DBMigration o Data Annotation, posso? – anIBMer

2

Sono d'accordo con marc_s - molto più sicuro avere il trigger (s) nel database. Nei database della mia azienda, richiedo che ogni campo contenga un campo Date_Modified, Date_Created e disponga persino di una funzione di utilità per creare automaticamente i trigger necessari.

Quando si utilizza con Entity Framework, ho trovato avevo bisogno di usare l'annotazione [DatabaseGenerated] con le mie classi POCO:

[Column(TypeName = "datetime2")] 
[DatabaseGenerated(DatabaseGeneratedOption.Computed)] 
public DateTime? Date_Modified { get; set; } 

[Column(TypeName = "datetime2")] 
[DatabaseGenerated(DatabaseGeneratedOption.Computed)] 
public DateTime? Date_Created { get; set; } 

Stavo tentando di utilizzare la mappatura stored procedure su un'entità, e EF è stato la creazione @Date_Modified , @Date_Created parametri sul mio inserto/aggiornamento sprocs ottenendo l'errore

La procedura o la funzione ha troppi argomenti specificati.

maggior parte degli esempi mostrano usando [NotMapped], che consentirà di selezione/Inserisci per lavorare, ma poi quei campi non verranno visualizzati quando tale entità viene caricato!

In alternativa è sufficiente assicurarsi che eventuali sproc contengano i parametri @Date_Modified, @Date_Created, ma questo va contro il progetto di utilizzare i trigger in primo luogo.