NET:工作流中如何动态解析路由规则 之 T4 + 动态编译
背景
在上篇文章中我介绍了如何用动态语言解释器执行路由规则,有很多朋友都给出了他们的选项,如下:
- 集成解释器(Iron、Javascript等)。
- 动态编译。
- 解析为Lamda表达式。
- 模板引擎。
因为我觉得动态编译很有意思,结合T4可能会更舒服,这篇文章就用这个思路重新实现一下如何解析路由规则。
思路
T4 + 动态编译 = 无限可能
如何使用动态编译解析这条规则(“LeaveDays>=5 && LeaveType=='病假'”)呢?思路有很多种,我立马想到的的有两种,将Leave的属性解析为某个方法的本地变量或方法所有类型的成员变量,下面就是动态编译后的方法:
1 public bool IsSatisfied(Leave entity) 2 { 3 var LeaveDays = entity.LeaveDays; 4 var LeaveType = entity.LeaveType; 5 return LeaveDays>=5 && LeaveType=="病假"; 6 }
实现(代码下载)
CSharpDynamicSpecification.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.CSharp; 8 using System.CodeDom.Compiler; 9 10 namespace DynamicExpressionStudy 11 { 12 public sealed class CSharpDynamicSpecification<T> : IDynamicSpecification<T> 13 { 14 private string _expression; 15 16 public CSharpDynamicSpecification(string expression) 17 { 18 _expression = expression; 19 } 20 21 public bool IsSatisfied(T entity) 22 { 23 var dynamicInstance = this.CompileAndCreateInstance(); 24 25 var result = dynamicInstance 26 .GetType() 27 .GetMethod("IsSatisfied") 28 .Invoke(dynamicInstance, new object[] { entity }); 29 30 return (bool)result; 31 } 32 33 private object CompileAndCreateInstance() 34 { 35 CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider(); 36 37 CompilerParameters cp = new CompilerParameters(); 38 cp.GenerateExecutable = false; 39 cp.GenerateInMemory = true; 40 cp.TreatWarningsAsErrors = false; 41 cp.ReferencedAssemblies.Add(Environment.CurrentDirectory + "\\DynamicExpressionStudy.exe"); 42 43 var templator = new DynamicSpecificationClassTemplate(typeof(T), _expression); 44 var sourceCode = templator.TransformText(); 45 46 CompilerResults cr = provider.CompileAssemblyFromSource(cp, sourceCode); 47 48 return Activator.CreateInstance(cr.CompiledAssembly.GetType("DynamicExpressionStudy." + templator.TempClassName)); 49 } 50 } 51 }
Program.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 DynamicExpressionStudy 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 var leave = new Leave 14 { 15 LeaveDays = 5, 16 LeaveType = "病假" 17 }; 18 19 var specification = new CSharpDynamicSpecification<Leave>("LeaveDays>=5 && LeaveType==\"病假\""); 20 21 var result = specification.IsSatisfied(leave); 22 23 Console.WriteLine(result); 24 } 25 } 26 27 public class Leave 28 { 29 public int LeaveDays { get; set; } 30 31 public string LeaveType { get; set; } 32 } 33 }
运行结果为:true。
备注
这些解析路由规则的思路,可以用在其他任何需要动态计算的场合,如:薪酬公式、考核公式、考勤公式等。