【发布时间】:2021-06-21 15:14:48
【问题描述】:
我正在尝试在 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", "GreaterThan", "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)
【问题讨论】:
-
表达式树不会在运行时编译为 C# 代码。它们被编译成中间语言 (IL),这也是 C# 代码编译成的。
标签: c# expression-trees rule-engine