Сигналы в Linq to Sql? - PullRequest
       20

Сигналы в Linq to Sql?

9 голосов
/ 18 июня 2009

Кто-нибудь знает способ сделать что-то похожее на Django signals с использованием LINQ to SQL?

Я пытаюсь записать, когда вставляются новые строки и когда обновляются определенные столбцы, поэтому я действительно просто хочу сигналы pre_save и post_save.

Я могу сделать это с некоторыми моделями, используя партиалы, определенные как OnFooIDChanging() и OnFooIDChanged() (где FooID - первичный ключ), но это не работает для моделей, первичный ключ которых не является личность или задается кодом.

Для них я мог бы использовать OnValidate(), но это было бы только pre_save, и это усложняет работу с базой данных, поскольку OnValidate() вызывается из DBContext.SubmitChanges(), что, конечно, не позволяет вторая SubmitChanges(), которая будет вызвана изнутри, делая, насколько я вижу, post_save практически невозможным.

Ответы [ 2 ]

1 голос
/ 19 июня 2009

У меня гораздо более простое решение, чем то, что я уже опубликовал, но оно все равно не сработало: переопределить SubmitChanges (ConflictMode faultMode):

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

В Entity Framework я делаю нечто похожее на то, что вы пытаетесь сделать: после сохранения сущности я вставляю новую запись в другую таблицу для целей аудита (это копия сущности перед изменениями). в контейнере сущностей EF (например, в контексте данных) имеется событие SaveChanges (), которое позволяет добавить его в текущий контекст перед сохранением изменений.

1 голос
/ 19 июня 2009

Хорошо, я полностью провалился в кроличью нору, но думаю, у меня есть довольно крутое решение:

Сначала добавьте обработчик событий в ваш контекст данных, который будет собирать все сигналы после сохранения, и скрыть метод Dispose, чтобы мы могли вызывать событие прямо перед тем, как утилизировать. (Обратите внимание, что я использую ключевое слово new вместо override. Это делает возможным вызов события.)

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

Далее, напишите Шаблон T4 , который проверяет файл dbml, который сгенерирует для вас Linq to Sql.

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

Для каждой таблицы в базе данных (и, следовательно, для каждого частичного класса) добавьте частичное с помощью следующих методов.

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

Также добавьте еще один partial MyDataContext через T4. Это добавит определения к частичным методам, которые предоставляет вам Linq to SQL (как упоминал Мерритт).

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

    // ...
}

Спрятать эти файлы в безопасное место, чтобы никто не пытался с ними связываться.

Ваша структура сигналов настроена. Теперь вы можете написать свои сигналы. Поместите их в Foo.cs или все вместе в Signals.cs файл:

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

Это немного сложно, поэтому, если что-то не имеет смысла, пожалуйста, оставьте комментарий, и я сделаю все возможное, чтобы решить его.

...