我正在建立一个给我带来一些令人头疼的规则引擎。当我尝试构建如下构造的表达式树时,问题出现了:
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);
}
它采用Criterion类定义的值列表:
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 :(得分:1)
您可以使用表达式调试视图来查看已构建的内容。对于您的表达式,它会显示此信息(在为您的param
指定名称&#34; x&#34;并使用Foo
属性为int
的{{1}}类进行调用:
Bar
正如您所看到的,在未分配的情况下使用了许多变量,这导致了您获得的异常。
有些注意事项:
.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);
}