Linq to Sql 中的信号?

发布于 2024-07-25 07:12:42 字数 666 浏览 5 评论 0原文

有谁知道有一种方法可以做类似于 Django 的 signals 使用 LINQ to SQL?

我试图记录何时插入新行以及何时更新某些列,所以我真的只想要 pre_savepost_save 信号。

我可以通过使用 OnFooIDChanging()OnFooIDChanged() 等定义的部分来对某些模型执行此操作(其中 FooID 是主键),但这对于主键不是身份或由代码设置的模型不起作用。

对于这些,我可能会使用 OnValidate(),但这只是 pre_save,并且它使得处理数据库变得困难,因为 OnValidate() code> 是从 DBContext.SubmitChanges() 调用的,这当然不允许从内部调用第二个 SubmitChanges(),从而使得 post_save代码>据我所知基本上不可能。

Does anyone know of a way to do something similar to Django's signals using LINQ to SQL?

I'm trying to record when new rows are inserted and when certain columns are updated, so I really just want pre_save and post_save signals.

I can kind of do it with some models by using the partials defined like OnFooIDChanging() and OnFooIDChanged() (where FooID is a primary key), but this doesn't work for models whose primary key is not an identity, or is set by code.

For those, I could possibly use OnValidate(), but that would only be pre_save, and it makes dealing with the database tough, since OnValidate() is called from DBContext.SubmitChanges(), which of course doesn't allow a second SubmitChanges() to be called from within, making post_save basically impossible as far as I can see.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

童话里做英雄 2024-08-01 07:12:42

好吧,我已经彻底陷入了这个问题,但我认为我有一个非常酷的解决方案:

首先,向您的数据上下文添加一个事件处理程序,该处理程序将收集所有保存后信号并隐藏 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 模板,用于检查 Linq to Sql 为您生成的 dbml 文件。

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

还可以通过 T4 添加另一个 partial MyDataContext。 这将为 Linq to SQL 为您提供的部分方法添加定义(如 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);
    }

    // ...
}

将这些文件隐藏在安全的地方,这样就没有人试图弄乱它们。

您的信号框架已设置。 现在您可以编写信号了。 将它们放在 Foo.cs 中,或者全部放在 Signals.cs 文件中:

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

这有点复杂,所以如果有任何不明白的地方,请发表评论我会尽力解决这个问题。

Ok, I've gone completely down the rabbit hole on this one, but I think I have a pretty cool solution:

First, add an event handler to your data context that will collect all of the post-save signals and hide the Dispose method so that we can call the event right before we dispose. (Note that I use the new keyword instead of override. This makes calling the event possible.)

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

Next, write a T4 Template that inspects the dbml file that Linq to Sql generates for you.

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

For each table in the database (and thus each partial class), add on to the partial with the following methods.

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

Also add another partial MyDataContext via T4. This will be adding definitions to the partial methods that Linq to SQL gives you (as Merritt mentioned).

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

    // ...
}

Hide those files away somewhere safe, so no one tries to mess with them.

Your signals framework is set up. Now you can write your signals. Put these either in Foo.cs or all together in a Signals.cs file:

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

This is a bit complex, so if anything doesn't make sense, please leave a comment and I'll do my best to address it.

_畞蕅 2024-08-01 07:12:42

我有一个比我已经发布的解决方案更简单的解决方案,无论如何都不起作用:覆盖 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
          }
}

使用实体框架,我会执行类似于您尝试执行的操作:保存实体后,我插入出于审计目的进入不同表的新条目(它是更改之前实体的副本)。 EF 实体容器(如数据上下文)上有一个 SaveChanges() 事件,允许您在保存更改之前添加到当前上下文。

I have a much easier solution than what I already posted which didn't work anyway: override 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
          }
}

With the Entity Framework, I do something similar to what you are trying to do: after I save a entity, I insert a new entry into a different table for auditing purposes (it's a copy of the entity before the changes). there is a SaveChanges() event on the EF entities container (like the data context) that allows you to add to the current context before changes are saved.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文