.NET:工作流中如何动态解析路由规则,你肯定用得着
背景
做流程引擎最终避免不了一个问题:如何动态解析路由规则?
几乎所有的审批流程都要求支持条件路由,比如:请假天数大于xx天时某个领导审批,否则其它人审批。常见的解决方法有:一、动态编译;二、解释执行。这篇文章就讲解如何使用Javascript引擎解释执行。
思路
静态语言集成动态语言解释器这种模式,在业界已经有很多惯例,如:很多C++开发者都用Lua在运行时修改配置。因为我对Javascript比较熟悉,所以准备搜索一下Javascript的解释器。在NuGet中用Javascript关键字进行搜索,搜索到了第2页就找到了一个解释器,安装一下,准备尝试吧。
实现
几乎我接触的所有解释器引擎提供的API都很相似,所以这里我直接贴代码了。
CondiationCalculator.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 Jurassic; 8 9 namespace DynamicExpressionStudy 10 { 11 public static class CondiationCalculator 12 { 13 public static bool IsSatisfied(object entity, string expression) 14 { 15 var engine = new ScriptEngine(); 16 17 if (entity != null) 18 { 19 foreach (var property in entity.GetType().GetProperties()) 20 { 21 engine.SetGlobalValue(property.Name, property.GetValue(entity)); 22 } 23 } 24 25 return engine.Evaluate<bool>(expression); 26 } 27 28 public static bool IsSatisfied(Dictionary<string, object> variables, string expression) 29 { 30 var engine = new ScriptEngine(); 31 32 if (variables != null) 33 { 34 foreach (var variable in variables) 35 { 36 engine.SetGlobalValue(variable.Key, variable.Value); 37 } 38 } 39 40 return engine.Evaluate<bool>(expression); 41 } 42 } 43 }
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 { 14 var leave = new Leave 15 { 16 LeaveDays = 5, 17 LeaveType = "病假" 18 }; 19 20 var result = CondiationCalculator.IsSatisfied(leave, "LeaveDays>=5 && LeaveType=='病假'"); 21 22 Console.WriteLine(result); 23 } 24 25 { 26 var leave = new Dictionary<string, object> 27 { 28 { "LeaveDays", 5 }, 29 { "LeaveType", "病假" } 30 }; 31 32 var result = CondiationCalculator.IsSatisfied(leave, "LeaveDays>=5 && LeaveType=='病假'"); 33 34 Console.WriteLine(result); 35 } 36 } 37 } 38 39 public class Leave 40 { 41 public int LeaveDays { get; set; } 42 43 public string LeaveType { get; set; } 44 } 45 }
执行结果
备注
有朋友会说这种方式是不是速度会比较慢,确实是慢,不过考虑到一次只判断一个单据,还是可以接受的。
如果要求接近C#的速度,可以采用动态编译+缓存动态程序集的方式,我就用过一次。
【★更新★】整理发布本人所有博文中提供的代码与工具(Java)
(有些内容已更改,重新编辑整理并再次发布,望见谅)
为了更方便地管理博文中涉及的各种代码与工具资源,现在把这些资源迁移到 Google Code 中,有兴趣者可前往下载。
Java
1、《【五一呈献】Java 全能高效 MVC & REST 开发框架 Portal-Basic v3.1.1 正式发布》
摘要:Portal-Basic 是一套功能完备的高性能 Full-Stack Web 应用开发框架,内置可扩展的 MVC Web 基础架构和 DAO 数据库访问组件(内部已提供了 Hibernate、MyBatis 与 JDBC DAO 组件),集成了 Action 拦截、Form / Dao / Spring Bean 装配、国际化、文件上传下载和页面静态化等基础 Web 应用组件,提供高效灵活的纯 Jsp/Servlet API 编程模型,可完美整合 Spring,支持 Action Convention,能快速开发传统风格和 RESTful 风格应用程序,文档和示例完善,非常容易学习。
资源下载地址:请访问项目主页 ^_*
摘要:对于服务器的日常维护来说,日志清理是非常重要的事情,如果残留日志过多则严重浪费磁盘空间同时影响服务的性能。如果用手工方式进行清理,会花费太多时间,并且很多时候难以满足实际要求。例如:如何在每个星期六凌晨3点把超过2G大的日志文件进行切割,保留最新的100M日志记录?网上没有发现能满足本座要求的日志切割工具,因此花了一些闲暇时间自己写了一个。由于要在多个平台上使用,为了方便采用 Java 实现。本工具命名为 LogCutter,主要有以下特点:
-
- 支持 Linux、Mac 和 Windows 等所有常见操作系统平台
- 支持命令行交互式运行
- 支持后台非交互式运行(Linux/MAC 下使用 daemon 进程实现,Windows 用系统 Service 实现)
- 支持两种日志清理方式(删除日志文件或切割日志文件)
- 支持对 GB18030、UTF-8、UTF-16LE、UTF-16BE 等常用日志文件类型进行切割(不会发生切掉半个字符的情况)
- 高度可配置(程序执行周期、要删除的日志文件过期时间、要切割的日志文件阀值和保留大小等均可配置
资源下载地址:http://ldcsaa.googlecode.com/files/LogCutter.zip
摘要:文件上传和下载是 Web 应用中的一个常见功能,相信各位或多或少都曾写过这方面相关的代码。但本座看过不少人在实现上传或下载功能时总是不知不觉间与程序的业务逻辑纠缠在一起,因此,当其他地方要用到这些功能时则无可避免地 Copy / Pase,然后再进行修改。这样丑陋不堪的做法导致非常容易出错不说,更大的问题是严重浪费时间不断做重复类似的工作,这是本座绝不能容忍的。哎,人生苦短啊,浪费时间在这些重复工作身上实在是不值得,何不把这些时间省出来打几盘罗马或者踢一场球?为此,本座利用一些闲暇之时光编写了一个通用的文件上传和文件下载组件,实现方法纯粹是基于 JSP,没有太高的技术难度,总之老少咸宜 ^_^。现把设计的思路和实现的方法向各位娓娓道来,希望能起到抛砖引玉的效果,激发大家的创造性思维。
资源下载地址:http://code.google.com/p/portal-basic/downloads/list (作为 Portal-Basic 的一部分,代码在 com.bruce.util.http 包中)
4、《深度剖析:Java POJO Bean 对象与 Web Form 表单的自动装配》
摘要:时下很多 Web 框架 都实现了 Form 表单域与 Java 对象属性的自动装配功能,该功能确实非常有用,试想如果没这功能则势必到处冲积着 request.getParameter() 系列方法与类型转换方法的调用。重复代码量大,容易出错,同时又不美观,影响市容。现在的问题是,这些框架通过什么方法实现自动装配的?如果不用这些框架我们自己如何去实现呢?尤其对于那些纯 JSP/Servlet 应用,要是拥有自动装配功能该多好啊!本座深知各位之期盼,决定把自动装配的原理和实现方法娓娓道来。
资源下载地址:http://code.google.com/p/portal-basic/downloads/list (作为 Portal-Basic 的一部分,代码在 com.bruce.util 包中)