如何在运行时计算表达式参数?扩展规则引擎支持嵌套计算
首先要纠正一个小误解:表达式树不仅是可视化的数据结构,它本身就是可以编译成可执行代码的,你之前的实现已经在这么做了——现在只需要把Target字段里的计算式也转换成表达式树,和原有的成员表达式组合起来就行。下面分两部分解决你的问题:
一、扩展代码解析Target中的计算表达式
原来的代码把Target直接转成常量Expression.Constant,现在我们需要把Target的字符串(比如"30/2")解析成可执行的表达式树,再合并到规则表达式中。最省心的方式是用System.Linq.Dynamic.Core这个NuGet包,它能直接将字符串表达式转换成.NET表达式树,支持常见的算术运算、逻辑运算甚至对象属性引用。
步骤1:安装NuGet包
先安装System.Linq.Dynamic.Core:
Install-Package System.Linq.Dynamic.Core
步骤2:修改BuildExpr方法
修改原来的BuildExpr方法,把Target字符串解析成表达式,而不是直接转常量:
static Expression BuildExpr<T>(Rule r, ParameterExpression param) { var left = MemberExpression.Property(param, r.MemberName); var tProp = typeof(T).GetProperty(r.MemberName).PropertyType; // 解析Target中的计算表达式:支持纯计算式(如"30/2")或引用对象属性(如"Age + 5") var targetExpr = DynamicExpressionParser.ParseLambda( parameters: new[] { param }, resultType: tProp, expression: r.TargetValue); var right = targetExpr.Body; ExpressionType tBinary; if (ExpressionType.TryParse(r.Operator, out tBinary)) { // 构建二元运算表达式,比如u.Age > (30/2) return Expression.MakeBinary(tBinary, left, right); } else { var method = tProp.GetMethod(r.Operator); var tParam = method.GetParameters()[0].ParameterType; // 转换表达式类型以匹配方法参数 right = Expression.Convert(right, tParam); // 构建方法调用表达式,比如u.Tags.Contains(Age + 10) return Expression.Call(left, method, right); } }
这样,当Target是"(30 / 2)"时,DynamicExpressionParser会把它转换成对应的除法表达式树,最终生成的规则就是u.Age > (30/2),完全符合你的需求。如果以后需要更复杂的Target(比如引用用户对象的其他属性"Age + 5"),这个代码也能直接支持。
手动解析表达式(可选)
如果不想引入第三方库,也可以自己写一个简单的表达式解析器,处理加减乘除和括号。不过这需要实现词法分析和语法分析,比较繁琐,适合对依赖有严格要求的场景。比如可以用递归下降法实现一个小型解析器,把字符串转换成表达式树。
二、现成规则引擎推荐
如果不想自己维护规则引擎的复杂逻辑,有几个成熟的.NET规则引擎可以考虑:
- RulesEngine:微软维护的轻量规则引擎,支持从JSON/XML加载规则,也支持复杂表达式和嵌套条件,非常适合业务规则动态配置的场景。
- NRules:基于.NET的规则引擎,支持正向链推理,适合复杂的业务规则场景,规则可以用代码或者DSL定义。
- EasyRules:轻量、易扩展的规则引擎,支持注解式规则定义,适合简单到中等复杂度的规则场景。
- System.Linq.Dynamic.Core:严格来说它不是完整的规则引擎,但它的表达式解析能力已经能覆盖大部分简单到中等的规则需求,像你现在的场景用它扩展就足够了。
这些引擎都支持动态加载规则、复杂表达式计算,能节省你自己造轮子的时间。
内容的提问来源于stack exchange,提问作者Gillan

