【发布时间】:2017-01-16 11:56:55
【问题描述】:
我有一个基本规则引擎,我构建的方式与此处建议的路线非常相似:
How to implement a rule engine?
我已经根据进一步的要求对其进行了扩展,现在我需要评估复杂的类,例如
EvaluateRule("Transaction.IsOpen", "Equals", "true")
最基本形式的代码是:
var param = inputMessageType;
left = Expression.Property(param, memberName);
tProp = typeof(T).GetProperty(r.MemberName).PropertyType;
right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp));
return Expression.MakeBinary(tBinary, left, right);
为了评估复杂的类,我使用了类似于这里的方法:
我遇到的问题是,当我尝试使用类 (Transaction.IsOpen) 的属性评估规则时,我使用表达式右侧的根类型的类型得到它,但类型表达式左侧的复杂对象。
这会导致错误:
System.InvalidOperationException: The binary operator Equal is not defined for the types 'System.Func`2[Transaction,System.Boolean]' and 'System.Boolean'.
我该如何克服这个问题?我不是使用表达式树的专家,当示例偏离标准文档时,许多概念被证明难以掌握。
编辑:这是代码(我省略了一些特定于环境的内容,以便关注问题)
public Actions EvaluateRulesFromMessage(ClientEventQueueMessage message)
{
var ruleGroups = _ruleRepository.GetRuleList();
var actions = new Actions();
foreach (var ruleGroup in ruleGroups)
{
if (message.MessageType == "UI_UPDATE")
{
// clean up json object
JObject dsPayload = (JObject.Parse(message.Payload));
var msgParams = JsonConvert.DeserializeObject<UiTransactionUpdate>(message.Payload);
msgParams.RulesCompleted = msgParams.RulesCompleted ?? new List<int>();
var conditionsMet = false;
// process the rules filtering out the rules that have already been evaluated
var filteredRules = ruleGroup.Rules.Where(item =>
!msgParams.RulesCompleted.Any(r => r.Equals(item.Id)));
foreach (var rule in filteredRules)
{
Func<UiTransactionUpdate, bool> compiledRule = CompileRule<UiTransactionUpdate>(rule, msgParams);
if (compiledRule(msgParams))
{
conditionsMet = true;
}
else
{
conditionsMet = false;
break;
}
}
if (conditionsMet)
{
actions = AddAction(message, ruleGroup);
break;
}
}
}
return actions;
}
public Func<UiTransactionUpdate, bool> CompileRule<T>(Rule r, UiTransactionUpdate msg)
{
var expression = Expression.Parameter(typeof(UiTransactionUpdate));
Expression expr = BuildExpr<UiTransactionUpdate>(r, expression, msg);
// build a lambda function UiTransactionUpdate->bool and compile it
return Expression.Lambda<Func<UiTransactionUpdate, bool>>(expr, expression).Compile();
}
static Expression Eval(object root, string propertyString, out Type tProp)
{
Type type = null;
var propertyNames = propertyString.Split('.');
ParameterExpression param = Expression.Parameter(root.GetType());
Expression property = param;
string propName = "";
foreach (var prop in propertyNames)
{
property = MemberExpression.PropertyOrField(property, prop);
type = property.Type;
propName = prop;
}
tProp = Type.GetType(type.UnderlyingSystemType.AssemblyQualifiedName);
var param2 = MemberExpression.Parameter(tProp);
var e = Expression.Lambda(property, param);
return e;
}
static Expression BuildExpr<T>(Rule r, ParameterExpression param, UiTransactionUpdate msg)
{
Expression left;
Type tProp;
string memberName = r.MemberName;
if (memberName.Contains("."))
{
left = Eval(msg, memberName, out tProp);
}
else
{
left = Expression.Property(param, memberName);
tProp = typeof(T).GetProperty(r.MemberName).PropertyType;
}
ExpressionType tBinary;
if (ExpressionType.TryParse(r.Operator, out tBinary))
{
Expression right=null;
switch (r.ValueType) ///todo: this needs to be refactored to be type independent
{
case TargetValueType.Value:
right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp));
break;
}
// use a binary operation ie true/false
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);
}
}
【问题讨论】:
-
您当前的代码现在看起来如何?
-
我的基本代码中的左侧赋值基本上使用了类似于底部链接中答案的代码。这几乎就是产生错误的原因 - 我明白我为什么会收到错误,我只需要一个克服它的策略
-
查看成员名是否包含“.” ...如果是这样,您可以按点拆分并制作嵌套的 MemberExpressions ...msdn.microsoft.com/en-us/library/…
-
谢谢 - 这就是我正在做的 - 问题是类型不匹配。但是我没有使用成员表达式,因此将探索该选项
-
实际上 - 我已经尝试过了(请参阅第二个链接),这仍然是我的问题所在。我应该对 MemberExpression 做些什么特别的事情吗?
标签: c# expression-trees