2009-06-17 22 views
9

Qualcuno sa di un modo per fare qualcosa di simile a Django's signals usando LINQ to SQL?Segnali in Linq a Sql?

Sto provando a registrare quando vengono inserite nuove righe e quando alcune colonne vengono aggiornate, quindi voglio davvero solo i segnali pre_save e post_save.

posso tipo di farlo con alcuni modelli utilizzando i parziali definiti come OnFooIDChanging() e OnFooIDChanged() (dove FooID è una chiave primaria), ma questo non funziona per i modelli la cui chiave primaria non è un'identità, o è impostato per codice.

Per chi, avrei potuto usare OnValidate(), ma che sarebbe solo essere pre_save, e mi fa fare con il database dura, dal momento che si chiama OnValidate() da DBContext.SubmitChanges(), che ovviamente non permette una seconda SubmitChanges() di essere chiamato da all'interno, rendendo praticamente impossibile post_save per quanto posso vedere.

risposta

1

Ok, sono andato fino in fondo nella tana del coniglio su questo, ma credo di avere una soluzione piuttosto fresco:

In primo luogo, aggiungere un gestore di eventi al vostro contesto dati che raccoglierà tutti i post -Salvia i segnali e nascondi il metodo Dispose in modo che possiamo chiamare l'evento giusto prima di sbarazzarci. (Si noti che uso la parola al posto di overridenew. Questo rende chiamando possibile l'evento.)

partial class MyDataContext 
{ 
    internal delegate void PostSaveHandler(); 
    internal event PostSaveHandler PostSave; 

    // This method hides the underlying Dispose because we need to call PostSave. 
    public new void Dispose(bool disposing) 
    { 
     // Obviously necessary error handling omitted for brevity's sake 
     PostSave(); 
     base.Dispose(disposing); 
    } 
} 

Avanti, scrivere un T4 Template che esamini il fascicolo dbml che LINQ to SQL genera per voi.

<# 
var dbml = XDocument.Load(@"MyDataContext.dbml"); 
var name = XName.Get("Type", "http://schemas.microsoft.com/linqtosql/dbml/2007"); 
var tables = from t in dbml.Descendants(name) select t.Attribute("Name").Value; 
foreach(var table in tables) 
{ 
#> 
    ... 

Per ogni tabella nel database (e quindi ogni classe parziale), aggiungere al parziale con i seguenti metodi.

public partial class Foo 
{ 
    internal void OnInsert(MyDataContext db) { 
     PreInsert(); 
     db.PostSave += delegate { PostInsert(); }; 
    } 
    internal void OnUpdate(MyDataContext db) { 
     PreUpdate(); 
     db.PostSave += delegate { PostUpdate(); }; 
    } 
    internal void OnDelete(MyDataContext db) { 
     PreDelete(); 
     db.PostSave += delegate { PostDelete(); }; 
    } 
    partial void PreInsert(); 
    partial void PostInsert(); 
    partial void PreUpdate(); 
    partial void PostUpdate(); 
    partial void PreDelete(); 
    partial void PostDelete(); 
} 

// repeat for all tables 

Aggiungere un altro partial MyDataContext tramite T4. Questo aggiungerà definizioni ai metodi parziali forniti da Linq a SQL (come menzionato da Merritt).

public partial class MyDataContext 
{ 
    // Add these three partial methods for each table 
    partial void InsertFoo(Foo foo) 
    { 
     foo.OnInsert(this); 
     ExecuteDynamicInsert(foo); 
    } 
    partial void UpdateFoo(Foo foo) 
    { 
     foo.OnUpdate(this); 
     ExecuteDynamicUpdate(foo); 
    } 
    partial void DeleteFoo(Foo foo) 
    { 
     foo.OnDelete(this); 
     ExecuteDynamicDelete(foo); 
    } 

    // ... 
} 

Nascondere questi file in un posto sicuro, in modo che nessuno provi a fare confusione con loro.

Il quadro dei segnali è impostato. Ora puoi scrivere i tuoi segnali. Mettete questi sia in Foo.cs o tutti insieme in un file Signals.cs:

partial class Foo 
{ 
    partial void PostInsert() 
    { 
     EventLog.AddEvent(EventType.FooInserted, this); 
    } 
} 

Questo è un po 'complessa, quindi se qualcosa non ha senso, si prega di lasciare un commento e farò del mio meglio per affrontarlo.

1

ho una soluzione molto più semplice di quello che ho già postato che non ha funzionato in ogni caso: SubmitChanges di override (ConflictMode failureMode):

partial class MyDataContext 
{ 
    // SubmitChanges() calls this method after inserting default value for param 
    public override void SubmitChanges(ConflictMode failureMode) 
    { 

      // Pre-Submit Changes 

      //Updates    
      for (int changeCounter = 0; changeCounter < this.GetChangeSet().Updates.Count; changeCounter++) 
      {     
       var modifiedEntity = this.GetChangeSet().Updates[changeCounter];     
       // Do something, for example: 
       // var tableXEntry = new TableX() { Prop1 = "foo" }; 
       // this.tableXEntries.InsertOnSubmit(tableXEntry); 
      }    

      //Inserts    
      for (int changeCounter = 0; changeCounter < this.GetChangeSet().Inserts.Count; changeCounter++)    
      {     
       object modifiedEntity = this.GetChangeSet().Inserts[changeCounter];     
       // Do Something 
      } 


      // Submit Changes 
      base.SubmitChanges(failureMode); 


      // Post Submit Changes 

      //Updates    
      for (int changeCounter = 0; changeCounter < this.GetChangeSet().Updates.Count; changeCounter++) 
      {     
       var modifiedEntity = this.GetChangeSet().Updates[changeCounter];     
       // Do something, for example: 
       // var tableXEntry = new TableX() { Prop1 = "foo" }; 
       // this.tableXEntries.InsertOnSubmit(tableXEntry); 
      }    

      //Inserts    
      for (int changeCounter = 0; changeCounter < this.GetChangeSet().Inserts.Count; changeCounter++)    
      {     
       object modifiedEntity = this.GetChangeSet().Inserts[changeCounter];     
       // Do Something 
      } 
} 

Con Entity Framework, faccio qualcosa di simile a quello che si sta cercando fare: dopo aver salvato un'entità, inserisco una nuova voce in una tabella diversa ai fini del controllo (è una copia dell'entità prima delle modifiche). c'è un evento SaveChanges() sul contenitore delle entità EF (come il contesto dati) che consente di aggiungere al contesto corrente prima che le modifiche vengano salvate.