2016-09-08 102 views
0

我有我建立在一个非常类似的方式向路线的基本规则引擎这里建议:评估复杂的表达式树

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);  

为了评估复杂的类,我采用了类似于此的方法:

https://stackoverflow.com/questions/16544672/dynamically-evaluating-a-property-string-with-expressions

具有即时通讯的问题是,当我尝试评价一类的属性规则(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); 
    } 
} 
+0

请问你当前的代码看看现在? –

+0

我的基本代码中的左侧分配本质上使用与底部链接中的答案类似的代码。这几乎是什么产生的错误 - 我明白为什么我得到错误,我只需要一个战略,以克服它 – KerSplosh

+0

看看成员名称,如果它包含一个“。” ...如果是这样,你可以按点分割并嵌套MemberExpressions ... https://msdn.microsoft.com/en-us/library/system.linq.expressions.memberexpression(v=vs.110)。 aspx – DarkSquirrel42

回答

2

示例代码不包括在方案中使用的所有数据类型,所以它的很难说它的位置在哪里,但是从例外情况System.Func'2[Transaction,System.Boolean]' and 'System.Boolean很明显,您左边的代表需要Transaction,并且返回boolFunc<Transaction, bool>),而右边则只有bool

这不是可以比较Func<Transaction, bool>bool,但它是可以调用的函数,并比较其结果:

Func<Transaction, bool> func = ...; 
bool comparand = ...; 
Transaction transaction = ...; 
if (func(transaction) == comparand) { ... } 

什么翻译成表达式树:

Expression funcExpression = ... /*LambdaExpression<Func<Transaction,bool>>*/; 
Expression comparandExpression = Expression.Constant(true); 
Expression transactionArg = /*e.g.*/Expression.Constant(transaction); 
Expression funcResultExpression = Expression.Call(funcExpression, "Invoke", null, transactionArg); 
Expression equalityTestExpression = Expression.Equal(funcResultExpression, comparandExpression); 
+0

感谢您的答案@Serge谢苗诺夫,但考虑到布尔只是一个例子,它可以是任何类型 - 我如何使funcExpression泛型比较? – KerSplosh

+0

在这种情况下,它不能只是通用的,因为你有一个函数的输入参数 - 交易 - 它应该来自某处。如果可以放在一起可以编译并显示问题的独立控制台应用程序,我可以帮忙。 –

+0

我会看看我能做些什么。我想我需要重新思考,因为我已经对这段代码感到困惑 – KerSplosh