问题描述
我正在尝试在 c# 中构建一个规则引擎,其中用户以 JSON 格式定义规则,并在运行时将其编译为 C#
代码。我正在关注这篇文章
http://coding-time.blogspot.com/2011/07/how-to-implement-rule-engine-in-c.html
一切正常,但这个规则引擎似乎只适用于 AND 条件。我找不到任何放置 OR 条件的方法。
这是我的代码:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
public class Program
{
public class ProductFamily
{
public int MotorHP
{
get;
set;
}
public String StationType
{
get;
set;
}
}
static Expression BuildExpr<T>(Rule r,ParameterExpression param)
{
var left = MemberExpression.Property(param,r.MemberName);
var tProp = typeof(T).GetProperty(r.MemberName).PropertyType;
ExpressionType tBinary;
// is the operator a kNown .NET operator?
if (ExpressionType.TryParse(r.Operator,out tBinary))
{
var right = Expression.Constant(Convert.ChangeType(r.TargetValue,tProp));
// use a binary operation,e.g. 'Equal' -> 'u.Age == 15'
return Expression.MakeBinary(tBinary,left,right);
}
else
{
var method = tProp.getmethod("Contains",new[] { typeof(string) });
var tParam = method.GetParameters()[0].ParameterType;
var right = Expression.Constant(Convert.ChangeType(r.TargetValue,tParam));
// use a method call,e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
return Expression.Call(left,method,right);
}
}
public static void Main()
{
string json = File.ReadAllText(@"TextFile1.txt");
RulesManager ruleManagerObj = JsonConvert.DeserializeObject<RulesManager>(json);
//List<Rule> rulesObj = new List<Rule>
//{
// new Rule("MotorHP","GreaterThan","20"),// new Rule("StationType","Equal","Horizontal Centrifugal")
//};
//RulesManager ruleObj = new RulesManager()
//{
// rules = rulesObj,// Message = "Test Failed!"
//};
var productFamily1 = new ProductFamily
{
MotorHP = 21,StationType = "Horizontal Cfugall"
}
;
var productFamily2 = new ProductFamily
{
MotorHP = 21,}
;
var productFamily3 = new ProductFamily
{
MotorHP = 13,};
//var rule = new Rule("MotorHP","20");
//Func<ProductFamily,bool> compiledRule = CompileRule<ProductFamily>(rule);
//bool isMatch = compiledRule(productFamily1);
// Compile all the rules once.
var compiledRules = ruleManagerObj.rules.Select(r => CompileRule<ProductFamily>(r)).ToList();
if (!compiledRules.Any(rule => rule(productFamily1)))
Console.WriteLine(ruleManagerObj.Message);
Console.ReadLine();
}
public static Func<T,bool> CompileRule<T>(Rule r)
{
var param = Expression.Parameter(typeof(ProductFamily));
Expression expr = BuildExpr<T>(r,param);
// build a lambda function User->bool and compile it
return Expression.Lambda<Func<T,bool>>(expr,param).Compile();
}
public class RulesManager
{
//Func<ProductFamily,Boolean> Matches { get; set; }
public List<Rule> rules { get; set; }
public Level Level { get; set; } // Level is an enum
public string Message { get; set; }
}
public class Rule
{
public string MemberName
{
get;
set;
}
public string Operator
{
get;
set;
}
public string TargetValue
{
get;
set;
}
public Rule(string MemberName,string Operator,string TargetValue)
{
this.MemberName = MemberName;
this.Operator = Operator;
this.TargetValue = TargetValue;
}
}
public enum Level
{
Error = 1
}
}
Json 字符串:
{
"rules": [
{
"MemberName": "MotorHP","Operator": "GreaterThan","TargetValue": "20"
},{
"MemberName": "StationType","Operator": "Contains","TargetValue": "Horizontal Centrifugal"
}
],"Level": 1,"Message": "Test Failed!"
}
有什么方法可以在评估表达式或规则时添加 OR 条件或条件组合。
示例:
(rule1 && rule2 && ... ruleN) && (rule1 || rule2 || ... ruleM)
解决方法
您可以使用 Expression.AndAlso
和 Expression.OrElse
。
例如,表达式 (rule1 && rule2) && (rule3 || rule4 || rule5)
将构造为:
Expression.AndAlso(
Expression.AndAlso(rule1,rule2),Expression.OrElse(Expression.OrElse(rule3,rule4),rule5)
);