幸福框架:在应用层实现触发器

背景

企业应用开发过程中经常面对一些非功能型需求,如:自动收集和设置审计信息、索引和关系约束,有些非功能需求当然可以用数据库自带的功能,如索引约束,但是应用层视乎也有必要重复一次,因为当违背这种约束的时候我们希望提示给用户友好的信息,如:‘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 }

运行效果

备注

这种触发器我在项目中有用过,虽然有所不足,如批量操作性能不高,但是在很多场景下,也减少了不少的重复代码。

 

posted on 2013-06-03 08:37  幸福框架  阅读(1880)  评论(10编辑  收藏  举报

导航

我要啦免费统计