2009-06-17 24 views
9

Czy ktoś wie o sposobie zrobić coś podobnego do Django's signals przy użyciu LINQ do SQL?Sygnały w Linq do Sql?

Próbuję nagrać, kiedy nowe wiersze są wstawiane i kiedy niektóre kolumny są aktualizowane, tak naprawdę chcę tylko pre_save i post_save sygnałów.

mogę niby zrobić z niektórych modeli według użyciu partials zdefiniowane jak OnFooIDChanging() i OnFooIDChanged() (gdzie FooID jest klucz podstawowy), ale to nie działa dla modeli, których klucz podstawowy nie jest tożsamością, albo jest ustawiony według kodu.

Dla tych, mógłbym użyć OnValidate(), ale to byłoby tylko pre_save, a to sprawia, że ​​do czynienia z bazą danych trudne, ponieważ OnValidate() jest wywoływana z DBContext.SubmitChanges(), co oczywiście nie zezwala drugi SubmitChanges() należne od wewnątrz, czyniąc post_save zasadniczo niemożliwym, o ile widzę.

Odpowiedz

1

Ok, mam już całkowicie w dół króliczej nory na ten jeden, ale myślę, że mam całkiem fajne rozwiązanie:

pierwsze, dodać obsługi zdarzeń do kontekstu danych, które będą zbierać wszystkie stanowiska -Zapisz sygnały i ukryj metodę Dispose, abyśmy mogli wywołać zdarzenie tuż przed pozbyciem się. (Zauważ, że używam new słowa kluczowego zamiast override. To sprawia, że ​​wywołanie zdarzenia możliwe.)

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

Następnie napisać T4 Template że kontroluje plik dbml że LINQ do SQL generuje dla Ciebie.

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

Dla każdej tabeli w bazie danych (a tym samym dla każdej klasy częściowej) dodaj do części z następującymi metodami.

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 

Dodaj również inny partial MyDataContext przez T4. To doda definicje do częściowych metod, które Linq do SQL daje (jak wspomniał 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); 
    } 

    // ... 
} 

Ukryj te pliki w bezpiecznym miejscu, aby nikt nie próbował z nimi zadzierać.

Twoje ramy sygnałów są skonfigurowane. Teraz możesz pisać swoje sygnały. Umieścić je zarówno w Foo.cs lub wszystkie razem w pliku Signals.cs:

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

To jest trochę skomplikowane, więc jeśli coś nie ma sensu, proszę zostawić komentarz i zrobię co w mojej mocy, aby go rozwiązać.

1

Mam dużo prostsze rozwiązanie niż to, co już pisał który nie działa i tak: SubmitChanges ręcznym (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 
      } 
} 

Z Entity Framework, zrobić coś podobnego do tego, co starają Czynność: po zapisaniu encji wstawiam nowy wpis do innej tabeli do celów audytu (jest to kopia obiektu przed zmianami). istnieje zdarzenie SaveChanges() na kontenerze elementów EF (np. kontekście danych), które umożliwia dodanie do bieżącego kontekstu przed zapisaniem zmian.