2009-06-17 17 views
9

Kennt jemand eine Möglichkeit, ähnlich wie Django's signals mit LINQ to SQL zu tun?Signale in Linq zu Sql?

Ich versuche aufzeichnen, wenn neue Zeilen eingefügt werden und wenn bestimmte Spalten aktualisiert werden, so will ich wirklich nur pre_save und post_save Signale.

ich kann Art tut es bei einigen Modellen durch die partials wie OnFooIDChanging() und OnFooIDChanged() definiert werden (wobei FooID einen Primärschlüssel ist), aber das funktioniert nicht für Modelle, dessen Primärschlüssels ist nicht eine Identität oder eingestellt ist nach Code.

Für diejenigen, ich möglicherweise OnValidate() verwenden könnte, aber das wäre nur pre_save, und es macht mit der Datenbank harten Umgang, da OnValidate() von DBContext.SubmitChanges() genannt wird, was natürlich nicht erlaubt ein zweites SubmitChanges() aus aufgerufen werden innerhalb, post_save grundsätzlich unmöglich machen, soweit ich sehen kann.

Antwort

1

Ok, ich habe auf diesen einen vollständig in den Kaninchenbau gegangen, aber ich glaube, ich habe eine ziemlich coole Lösung:

Zuerst einen Event-Handler, um Ihre Daten Kontext hinzufügen, dass alle von der Post sammeln -Speichern Sie Signale und blenden Sie die Methode Dispose aus, damit wir das Ereignis unmittelbar vor der Entsorgung aufrufen können. (Beachten Sie, dass ich das new Schlüsselwort verwenden statt override. Dies macht die Veranstaltung möglich anrufen.)

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

Als nächstes schreiben einen T4 Template, die die dbml Datei überprüft, die LINQ to SQL für Sie generiert.

<# 
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) 
{ 
#> 
    ... 

Fügen Sie für jede Tabelle in der Datenbank (und damit für jede Teilklasse) die partielle mit den folgenden Methoden hinzu.

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 

Fügen Sie auch einen weiteren partial MyDataContext über T4 hinzu. Dies wird Definitionen zu den partiellen Methoden hinzufügen, die Linq to SQL Ihnen gibt (wie Merritt erwähnt).

Verstecken Sie diese Dateien irgendwo an einem sicheren Ort, damit niemand versucht, sich mit ihnen anzulegen.

Ihr Signalframework ist eingerichtet. Jetzt können Sie Ihre Signale schreiben. Setzen Sie diese entweder in Foo.cs oder alle zusammen in einer Signals.cs Datei:

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

Dies ist ein wenig kompliziert, so dass, wenn etwas nicht sinnvoll ist, lassen Sie einen Kommentar und ich werde mein Bestes tun, um es zu behandeln.

1

Ich habe eine viel einfachere Lösung als das, was ich bereits geschrieben, die sowieso nicht funktionieren: Überschreibung SubmitChanges (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 
      } 
} 

Mit dem Entity Framework, das tue ich etwas Ähnliches, was Sie versuchen, zu tun: Nachdem ich eine Entität gespeichert habe, füge ich einen neuen Eintrag in eine andere Tabelle für Überwachungszwecke ein (es ist eine Kopie der Entität vor den Änderungen). Es gibt ein SaveChanges() -Ereignis für den EF-Entitätscontainer (wie den Datenkontext), mit dem Sie dem aktuellen Kontext vor dem Speichern von Änderungen hinzufügen können.