2016-05-12 46 views
0

我正在構建一個規則引擎,給我一些頭痛的問題。問題是當我嘗試建立表達式樹的構造如下:表達式樹編譯失敗,由於未定義的變量?

public Tuple<Expression, ParameterExpression> BuildExpression<T>(string propertyName, Enums.Operator ruleOperator, 
     ParameterExpression parameterExpression, List<object> values) 
    { 
     ParameterExpression listExpression = Expression.Parameter(typeof(List<object>)); 
     ParameterExpression counterExpression = Expression.Parameter(typeof(int)); 
     ParameterExpression toExpression = Expression.Parameter(typeof(int)); 
     ParameterExpression arrayExpression = Expression.Parameter(typeof(object[])); 
     ParameterExpression valueExpression = Expression.Parameter(typeof(object)); 
     ParameterExpression checkExpression = Expression.Parameter(typeof(T)); 
     ParameterExpression returnExpression = Expression.Parameter(typeof(bool)); 
     MemberExpression body = null; 

     foreach (var member in propertyName.Split('.')) 
     { 
      body = MemberExpression.Property(parameterExpression, member); 
     } 

     Expression expression = body.Expression; 
     var type = expression.Type; 
     ParameterExpression propertyExpression = Expression.Parameter(type); 
     ParameterExpression localPropertyExpression = Expression.Parameter(type); 

     LabelTarget breakLabel = Expression.Label(); 
     PropertyInfo result = typeof(List<object>).GetProperty("Count"); 
     MethodInfo toArray = typeof(List<object>).GetMethod("ToArray"); 
     MethodInfo getGetMethod = result.GetGetMethod(); 
     ConstantExpression constantExpression = Expression.Constant(true); 
     if (ruleOperator == Enums.Operator.NotFoundIn) 
     { 
      constantExpression = Expression.Constant(false); 
     } 

     Expression loop = Expression.Block(
      new ParameterExpression[] { toExpression, arrayExpression, valueExpression, counterExpression, 
      returnExpression, propertyExpression, localPropertyExpression, listExpression }, 
      Expression.Assign(listExpression, Expression.Constant(values)), 
      Expression.Assign(toExpression, Expression.Call(listExpression, getGetMethod)), 
      Expression.Assign(arrayExpression, Expression.Call(listExpression, toArray)), 
      Expression.Assign(propertyExpression, expression), 
      Expression.Loop(
       Expression.IfThenElse(
        Expression.LessThan(counterExpression, toExpression), 
        Expression.Block(
         Expression.Assign(valueExpression, Expression.ArrayAccess(arrayExpression, counterExpression)), 
         Expression.Assign(localPropertyExpression, expression), 
         Expression.IfThen(
          Expression.Equal(propertyExpression, localPropertyExpression), 
          Expression.Block(Expression.Assign(returnExpression, constantExpression), 
           Expression.Break(breakLabel))), 
         Expression.Assign(Expression.ArrayAccess(arrayExpression, counterExpression), checkExpression), 
         Expression.PostIncrementAssign(counterExpression)), 
        Expression.Break(breakLabel) 
        ), breakLabel 
       ), 
       Expression.And(returnExpression, constantExpression) 
      ); 

     return new Tuple<Expression, ParameterExpression>(Expression.Block(loop), checkExpression); 
    } 

這需要通過標準類定義的值的列表:

public class Criterion 
{ 
    public List<object> Values { get; set; } 

    public string PropertyName { get; set; } 

    public Enums.Operator Operator_ { get; set; } 
} 

,然後通過下面的方法編譯:

public Func<T, bool>[] CombineRules<T>(Criterion[] criteria) 
     { 
      var list = new List<Func<T, bool>>(); 
      foreach (var criterion in criteria) 
      { 
       var expressionBuilder = new ExpressionBuilder(); 
       var param = Expression.Parameter(typeof(T)); 
       var expression = 
        expressionBuilder.BuildExpression<T>(criterion.PropertyName, criterion.Operator_, param, criterion.Values); 
       var func = Expression.Lambda<Func<T, bool>>(
        expression.Item1, expression.Item2).Compile(); 
       list.Add(func); 
      } 

      return list.ToArray(); 
     } 

但是,編譯失敗,出現以下異常:

System.InvalidOperationException: variable '' of type 'SnippInteractive.Web.Common.Models.V2.LineItem' referenced from scope '', but it is not defined 

如果有人有任何有用的建議,我會非常感激。

感謝您的閱讀。

+0

有與該代碼有很多潛在的問題,你能不能樣本手冊(即不使用'表達'方法)lambda呈現你正在嘗試構建的東西。 –

回答

1

您可以使用表達式調試視圖來查看您已經構建的內容。對於你的表情,就說明這個(您param指定名稱爲「X」後,並呼籲與具有int財產Bar簡單Foo類):

.Block() { 
    .Block(
     System.Int32 $var1, 
     System.Object[] $var2, 
     System.Object $var3, 
     System.Int32 $var4, 
     System.Boolean $var5, 
     ConsoleApplication6.Foo $var6, 
     ConsoleApplication6.Foo $var7, 
     System.Collections.Generic.List`1[System.Object] $var8) { 
     $var8 = .Constant<System.Collections.Generic.List`1[System.Object]>(System.Collections.Generic.List`1[System.Object]); 
     $var1 = .Call $var8.get_Count(); 
     $var2 = .Call $var8.ToArray(); 
     $var6 = $x; 
     .Loop { 
      .If ($var4 < $var1) { 
       .Block() { 
        $var3 = $var2[$var4]; 
        $var7 = $x; 
        .If ($var6 == $var7) { 
         .Block() { 
          $var5 = True; 
          .Break #Label1 { } 
         } 
        } .Else { 
         .Default(System.Void) 
        }; 
        $var2[$var4] = $var9; 
        $var4++ 
       } 
      } .Else { 
       .Break #Label1 { } 
      } 
     } 
     .LabelTarget #Label1:; 
     $var5 & True 
    } 
} 

正如你可以看到,有很多的變量沒有被使用分配,這是造成你得到的例外。

需要注意以下幾點:

  • 的代碼是非常難以遵循,由於變量命名
  • 您應該使用Expression.Variable定義表達式中的變量
  • 這是很好的名字分配給你的表情參數/變量爲更好的可讀性的調試視圖輸出
  • 該代碼是絕對沒有做什麼打算

從我看到的,看起來像你正在試圖建立像object.property in/not in values表達式。如果這是真的,這裏是你如何能做到這一點:

public Tuple<Expression, ParameterExpression> BuildExpression<T>(string propertyName, Enums.Operator ruleOperator, ParameterExpression target, List<object> values) 
{ 
    var property = propertyName.Split('.').Aggregate((Expression)target, Expression.PropertyOrField); 
    var propertyValue = Expression.Variable(property.Type, "propertyValue"); 
    var array = Expression.Variable(typeof(object[]), "array"); 
    var length = Expression.Variable(typeof(int), "length"); 
    var index = Expression.Variable(typeof(int), "index"); 
    var value = Expression.Variable(typeof(object), "value"); 
    var result = Expression.Variable(typeof(bool), "result"); 
    var endLoop = Expression.Label("endLoop"); 
    bool success = ruleOperator != Enums.Operator.NotFoundIn; 
    Expression body = Expression.Block 
    (
     new ParameterExpression[] { propertyValue, array, length, index, result }, 
     Expression.Assign(propertyValue, property), 
     Expression.Assign(array, Expression.Call(Expression.Constant(values), "ToArray", Type.EmptyTypes)), 
     Expression.Assign(length, Expression.ArrayLength(array)), 
     Expression.Assign(index, Expression.Constant(0)), 
     Expression.Assign(result, Expression.Constant(!success)), 
     Expression.Loop 
     (
      Expression.IfThenElse 
      (
       Expression.LessThan(index, length), 
       Expression.Block 
       (
        Expression.IfThen 
        (
         Expression.Equal(propertyValue, Expression.Convert(Expression.ArrayIndex(array, index), property.Type)), 
         Expression.Block 
         (
          Expression.Assign(result, Expression.Constant(success)), 
          Expression.Break(endLoop) 
         ) 
        ), 
        Expression.PostIncrementAssign(index) 
       ), 
       Expression.Break(endLoop) 
      ), 
      endLoop 
     ), 
     result 
    ); 
    return Tuple.Create(body, target); 
} 

它輸出這樣的:

.Block(
    System.Int32 $propertyValue, 
    System.Object[] $array, 
    System.Int32 $length, 
    System.Int32 $index, 
    System.Boolean $result) { 
    $propertyValue = $x.Bar; 
    $array = .Call .Constant<System.Collections.Generic.List`1[System.Object]>(System.Collections.Generic.List`1[System.Object]).ToArray(); 
    $length = $array.Length; 
    $index = 0; 
    $result = False; 
    .Loop { 
     .If ($index < $length) { 
      .Block() { 
       .If ($propertyValue == (System.Int32)$array[$index]) { 
        .Block() { 
         $result = True; 
         .Break endLoop { } 
        } 
       } .Else { 
        .Default(System.Void) 
       }; 
       $index++ 
      } 
     } .Else { 
      .Break endLoop { } 
     } 
    } 
    .LabelTarget endLoop:; 
    $result 
} 
+0

是的,我應該提到我正在將一個值與一個值列表進行比較。變量的使用使調試過程更容易管理,非常感謝您的幫助! – shanomacadaemia

相關問題