gpt4 book ai didi

c# - 如何实现规则引擎?

转载 作者:IT王子 更新时间:2023-10-29 03:29:55 26 4
gpt4 key购买 nike

我有一个存储以下内容的数据库表:

RuleID  objectProperty ComparisonOperator  TargetValue
1 age 'greater_than' 15
2 username 'equal' 'some_name'
3 tags 'hasAtLeastOne' 'some_tag some_tag2'

现在假设我有这些规则的集合:

List<Rule> rules = db.GetRules();

现在我还有一个用户实例:

User user = db.GetUser(....);

我将如何遍历这些规则、应用逻辑并执行比较等?

if(user.age > 15)

if(user.username == "some_name")

由于对象的属性(如“age”或“user_name”)以及比较运算符“great_than”和“equal”存储在表中,我怎么可能这样做?

C# 是一种静态类型语言,因此不确定如何前进。

最佳答案

此代码段将规则编译成快速可执行代码(使用 Expression trees )并且不需要任何复杂的 switch 语句:

(编辑:full working example with generic method)

public Func<User, bool> CompileRule(Rule r)
{
var paramUser = Expression.Parameter(typeof(User));
Expression expr = BuildExpr(r, paramUser);
// build a lambda function User->bool and compile it
return Expression.Lambda<Func<User, bool>>(expr, paramUser).Compile();
}

然后你可以写:

List<Rule> rules = new List<Rule> {
new Rule ("Age", "GreaterThan", "21"),
new Rule ( "Name", "Equal", "John"),
new Rule ( "Tags", "Contains", "C#" )
};

// compile the rules once
var compiledRules = rules.Select(r => CompileRule(r)).ToList();

public bool MatchesAllRules(User user)
{
return compiledRules.All(rule => rule(user));
}

这里是 BuildExpr 的实现:

Expression BuildExpr(Rule r, ParameterExpression param)
{
var left = MemberExpression.Property(param, r.MemberName);
var tProp = typeof(User).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 == 21'
return Expression.MakeBinary(tBinary, left, right);
} else {
var method = tProp.GetMethod(r.Operator);
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);
}
}

请注意,我使用“GreaterThan”而不是“greater_than”等 - 这是因为“GreaterThan”是运算符的 .NET 名称,因此我们不需要任何额外的映射。

如果您需要自定义名称,您可以构建一个非常简单的字典并在编译规则之前翻译所有运算符:

var nameMap = new Dictionary<string, string> {
{ "greater_than", "GreaterThan" },
{ "hasAtLeastOne", "Contains" }
};

为简单起见,代码使用了 User 类型。您可以将 User 替换为通用类型 T 以获得 generic Rule compiler对于任何类型的对象。此外,代码应处理错误,例如未知的运算符(operator)名称。

请注意,即使在引入表达式树 API 之前,使用 Reflection.Emit 也可以动态生成代码。 LambdaExpression.Compile() 方法在幕后使用 Reflection.Emit(您可以使用 ILSpy 查看)。

关于c# - 如何实现规则引擎?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6488034/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com