【问题标题】:Signals in Linq to Sql?Linq to Sql中的信号?
【发布时间】:2023-03-29 01:55:02
【问题描述】:

有谁知道使用 LINQ to SQL 做类似于Django's signals 的方法吗?

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

我可以通过使用像 OnFooIDChanging()OnFooIDChanged() (其中 FooID 是主键)这样定义的部分来处理某些模型,但这不适用于主键不是主键的模型身份,或由代码设置。

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

【问题讨论】:

    标签: asp.net-mvc linq-to-sql django-signals


    【解决方案1】:

    好的,我在这个问题上完全陷入了困境,但我认为我有一个非常酷的解决方案:

    首先,将事件处理程序添加到您的数据上下文中,该处理程序将收集所有保存后信号并隐藏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 Template 来检查 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);
        }
    }
    

    这有点复杂,所以如果有什么不明白的地方,请发表评论,我会尽力解决它。

    【讨论】:

      【解决方案2】:

      我有一个比我已经发布的解决方案更简单但无论如何都不起作用的解决方案:覆盖 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() 事件,允许您在保存更改之前添加到当前上下文。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多