幸福框架:在應用層實現觸發器


背景

企業應用開發過程中經常面對一些非功能型需求,如:自動收集和設置審計信息、索引和關系約束,有些非功能需求當然可以用數據庫自帶的功能,如索引約束,但是應用層視乎也有必要重復一次,因為當違背這種約束的時候我們希望提示給用戶友好的信息,如:‘xxx已經存在,xxx必須唯一’,這篇文章我就介紹一個簡單的方案應對這種需求。

思路

我覺得數據庫的觸發器是個好東西,應用層完全可以借用一下,我還認為如果我在應用層實現了觸發器,像一些前置條件和后置條件驗證也可以用觸發器實現(這塊我不是很清楚設計的是否合理,還是要引入另外一個繼承體系)。

實現

核心類

核心代碼

DefaultTriggerService.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 using Microsoft.Practices.ServiceLocation;
 8 
 9 using Happy.Domain;
10 
11 namespace Happy.Application.Trigger.Internal
12 {
13     internal sealed class DefaultTriggerService : ITriggerService
14     {
15         public void ExecuteBeforeInsert<TAggregateRoot>(TAggregateRoot aggregate)
16             where TAggregateRoot : AggregateRoot
17         {
18             var triggers = ServiceLocator
19                 .Current
20                 .GetAllInstances<ICreateTrigger<TAggregateRoot>>();
21 
22             foreach (var trigger in triggers)
23             {
24                 trigger.BeforeInsert(aggregate);
25             }
26         }
27 
28         public void ExecuteAfterInsert<TAggregateRoot>(TAggregateRoot aggregate)
29             where TAggregateRoot : AggregateRoot
30         {
31             var triggers = ServiceLocator
32                 .Current
33                 .GetAllInstances<ICreateTrigger<TAggregateRoot>>();
34 
35             foreach (var trigger in triggers)
36             {
37                 trigger.AfterInsert(aggregate);
38             }
39         }
40 
41         public void ExecuteBeforeUpdate<TAggregateRoot>(TAggregateRoot aggregate)
42             where TAggregateRoot : AggregateRoot
43         {
44             var triggers = ServiceLocator
45                 .Current
46                 .GetAllInstances<IUpdateTrigger<TAggregateRoot>>();
47 
48             foreach (var trigger in triggers)
49             {
50                 trigger.BeforeUpdate(aggregate);
51             }
52         }
53 
54         public void ExecuteAfterUpdate<TAggregateRoot>(TAggregateRoot aggregate)
55             where TAggregateRoot : AggregateRoot
56         {
57             var triggers = ServiceLocator
58                 .Current
59                 .GetAllInstances<IUpdateTrigger<TAggregateRoot>>();
60 
61             foreach (var trigger in triggers)
62             {
63                 trigger.AfterUpdate(aggregate);
64             }
65         }
66 
67         public void ExecuteBeforeDelete<TAggregateRoot>(TAggregateRoot aggregate)
68             where TAggregateRoot : AggregateRoot
69         {
70             var triggers = ServiceLocator
71                 .Current
72                 .GetAllInstances<IDeleteTrigger<TAggregateRoot>>();
73 
74             foreach (var trigger in triggers)
75             {
76                 trigger.BeforeDelete(aggregate);
77             }
78         }
79 
80         public void ExecuteAfterDelete<TAggregateRoot>(TAggregateRoot aggregate)
81             where TAggregateRoot : AggregateRoot
82         {
83             var triggers = ServiceLocator
84                 .Current
85                 .GetAllInstances<IDeleteTrigger<TAggregateRoot>>();
86 
87             foreach (var trigger in triggers)
88             {
89                 trigger.AfterDelete(aggregate);
90             }
91         }
92     }
93 }

自動管理樹形節點的路徑信息

代碼

ITreeNode.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Happy.Domain.Feature
 8 {
 9     /// <summary>
10     /// 樹的節點。
11     /// </summary>
12     public interface ITreeNode
13     {
14         /// <summary>
15         /// 節點ID。
16         /// </summary>
17         Guid Id { get; set; }
18 
19         /// <summary>
20         /// 父節點ID。
21         /// </summary>
22         Guid ParentId { get; set; }
23 
24         /// <summary>
25         /// 節點在樹中的路徑,如:/A/B/C/D,包含自己。
26         /// </summary>
27         string NodePath { get; set; }
28     }
29 }

TreeNodeTrigger.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 using Happy.ExtensionMethod;
 8 using Happy.Domain;
 9 using Happy.Domain.Feature;
10 using Happy.Application.Trigger;
11 
12 namespace Happy.EntityFramework.Trigger
13 {
14     public class TreeNodeTrigger<TUnitOfWork, TAgggregateRoot> : TriggerBase<TUnitOfWork, TAgggregateRoot>
15         where TUnitOfWork : UnitOfWork
16         where TAgggregateRoot : AggregateRoot, ITreeNode
17     {
18         public override void BeforeInsert(TAgggregateRoot aggregate)
19         {
20             var parentNodePath = this.GetParentNodePath(aggregate);
21 
22             aggregate.NodePath = parentNodePath + "/" + aggregate.Id;
23         }
24 
25         public override void BeforeUpdate(TAgggregateRoot aggregate)
26         {
27             var newParentNodePath = this.GetParentNodePath(aggregate);
28             var newNodePath = newParentNodePath + "/" + aggregate.Id;
29             var oldNodePath = aggregate.NodePath;
30 
31             if (oldNodePath == newNodePath)
32             {
33                 return;
34             }
35 
36             aggregate.NodePath = newNodePath;
37 
38             var table = typeof(TAgggregateRoot).Name.ToPluralize();
39             var sql = string.Format("UPDATE {0} SET NodePath = REPLACE(NodePath, {{0}}, {{1}}) WHERE NodePath LIKE {{2}}", table);
40             this.UnitOfWork.Database.ExecuteSqlCommand(sql, oldNodePath, newNodePath, oldNodePath + "/%");
41         }
42 
43         public override void BeforeDelete(TAgggregateRoot aggregate)
44         {
45             var table = typeof(TAgggregateRoot).Name.ToPluralize();
46             var sql = string.Format("DELETE FROM {0} WHERE NodePath LIKE {{0}}", table);
47             this.UnitOfWork.Database.ExecuteSqlCommand(sql, aggregate.NodePath + "/%");
48         }
49 
50         private string GetParentNodePath(TAgggregateRoot aggregate)
51         {
52             var table = typeof(TAgggregateRoot).Name.ToPluralize();
53             var sql = string.Format("SELECT NodePath FROM {0} WHERE Id = {{0}}", table);
54             return this.UnitOfWork.Database.SqlQuery<string>(sql, aggregate.ParentId).FirstOrDefault();
55         }
56     }
57 }

運行效果

備注

這種觸發器我在項目中有用過,雖然有所不足,如批量操作性能不高,但是在很多場景下,也減少了不少的重復代碼。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM