理解表达式树和参数评估

我正在尝试修改一个动态构建Contains表达式的表达式树,最终会产生类似SQL的表达式

P IN (123, 124, 125, 200, 201)

而是检查执行范围检查,最终导致SQL像

(P >= 123 AND P = 200 AND P <= 201)

我的解决方案基于这篇文章 。

 static public Expression<Func> BuildContainsExpression( Expression<Func> valueSelector, IEnumerable values) { // Removed for post: Input checks and edge cases var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue)))); var body = equals.Aggregate((accumulate, equal) => Expression.Or(accumulate, equal)); return Expression.Lambda<Func>(body, p); } 

如果我提供比较值,我可以让范围检查工作:

 long testValue = 5; List<KeyValuePair> ranges = new List<KeyValuePair>() { new KeyValuePair(3, 6), new KeyValuePair(10, 12), new KeyValuePair(20, 20), }; List rangeExpressions = new List(); foreach (var pair in ranges) { var greaterThanOrEqual = Expression.GreaterThanOrEqual(Expression.Constant(testValue), Expression.Constant(pair.Key)); var lessThanOrEqual = Expression.LessThanOrEqual(Expression.Constant(testValue), Expression.Constant(pair.Value)); var inRange = Expression.AndAlso(greaterThanOrEqual, lessThanOrEqual); rangeExpressions.Add(inRange); } var final = rangeExpressions.Aggregate((a, b) => Expression.Or(a, b)); var result = Expression.Lambda<Func>(final).Compile()(); 

但是,当我将该代码放入要与Linq一起使用的方法时,我无法理解如何从传入的表达式中获取用于比较的值。 该方法的签名是:

 Expression<Func> BuildRangeExpression( Expression<Func> valueSelector, IEnumerable values) 

它用作:

 Expression match = BuildRangeExpression(my => my.ProductCode, productCodes); var result = db.MyTypes.Where(match); 

我该如何评估?

 Expression<Func> valueSelector 

这样我就可以使用传递给BuildRangeExpression的值而不是我当前的硬编码值

 long testValue = 5; 

我认为博客文章中的代码正是您所需要的:您所要做的就是使用valueSelector.Body而不是Expression.Constant() ,并将原始参数添加到生成的表达式中:

 public static Expression> BuildRangeExpression( Expression> valueSelector, IEnumerable> values) { var p = valueSelector.Parameters.Single(); var equals = values.Select( tuple => Expression.AndAlso( Expression.GreaterThanOrEqual( valueSelector.Body, Expression.Constant(tuple.Item1)), Expression.LessThanOrEqual( valueSelector.Body, Expression.Constant(tuple.Item2)))); var body = equals.Aggregate(Expression.OrElse); return Expression.Lambda>(body, p); } 

使用Expression.Parameter

创建一个参数:

 var param = Expression.Parameter(typeof(TElement), "arg") 

而不是Expression.Constant(testvalue) ,你需要放置param

然后,你需要做:

 var result = Expression.Lambda>(final, param).Compile()