前一篇在介绍时,提到一个问题:如何根据用户的输入条件,动态构造这个过滤条件表达式呢?
Expression<Func<ProductExt,
bool>> predicate t => t.ProductName.Contains(
"che") && t.UnitPrice >= 22;
理想情况下,我希望可以像下面这样来构造predicate,这样,我们就可以使用&、| 、&=、|=来任意拼接过滤条件了:
1: Expression > predicate = null;
2: predicate (t => t.ProductName.Contains("che")) (t => t.UnitPrice >= 22);
但是理想与现实之间,似乎总有不可逾越的鸿沟……
前面的代码中,我们总是要写一常串Expression<Func<T, bool>>,写得都有点儿烦了,我妄想自定义一个类型,这样就不用每次都写这么长了,然后再针对这个自定义类型再进行运算符重载……然后我发现,Expression<Delegate>是密封类型(sealed),不给重载,没办法,只好老老实实的写,运算符重载也泡汤了。
无奈之下,只好实现了下面的这种方式,勉强凑合用着:
1: Expression <, bool>> predicate = null;
2: predicate = predicate.AndAlso(p => p.CompanyName.Length > 2)
3: .OrElse((p) => p.ProductName.Contains("che"))
4: .AndAlso(( p) => p.UnitPrice >= 22);
运算得到的结果如下:p => (((p.CompanyName.Length > 2) || p.ProductName.Contains("che")) && (p.UnitPrice >= 22))
有了这个OrElse和AndAlso扩展,我们就可以对Expression<Func<T, bool>>为所欲为了……
不过跟之前文章中提到的一样,这里有个限制:虽然我们的Expression中,第一个可以凡泛型参数可以传任意值(譬如传ProductExt或Products或其他的,可以不必要求是同一种类型),但条件中用到的对象属性(譬如CompanyName、ProductName、UnitPrice ),必须在T中存在同名属性。
2010-12-31更新:
更新了ParameterConverter类,之前对二元运算符的右侧和方法参数直接求值的,所以不能处理Expression中含有&& 和 || 等二元运算符、及方法参数中包含Expression参数(例如list.Contains(p.CompanyName))的情况;现在增加了参数访问计数,如果二元表达式&&和||、及方法的参数中不包含Expression参数,则进行求值,否则不求值。代码如下:
1 public class ParameterConverter : ExpressionVisitor 2 { 3 protected LambdaExpression SourceExpression { get ; set ; } 4 protected ParameterExpression Parameter { get ; set ; } 5 protected bool UseOuterParameter = false ; 6 7 public ParameterConverter(LambdaExpression expression) 8 { 9 this .SourceExpression = (LambdaExpression)expression; 10 } 11 12 public ParameterConverter(LambdaExpression expression, ParameterExpression parameter) : this (expression) 13 { 14 this .Parameter = parameter; 15 this .UseOuterParameter = true ; 16 } 17 18 public LambdaExpression Replace(Type targetType) 19 { 20 if ( this .SourceExpression == null ) 21 return null ; 22 23 if ( ! this .UseOuterParameter) 24 this .Parameter = Expression.Parameter(targetType, this .SourceExpression.Parameters[ 0 ].Name); 25 26 Expression body = this .Visit( this .SourceExpression.Body); 27 this .SourceExpression = Expression.Lambda(body, this .Parameter); 28 return this .SourceExpression; 29 } 30 31 protected override Expression VisitParameter(ParameterExpression p) 32 { 33 return this .Parameter; 34 } 35 36 protected override Expression VisitMemberAccess(MemberExpression m) 37 { 38 Expression exp = this .Visit(m.Expression); 39 PropertyInfo propertyInfo = m.Member as PropertyInfo; 40 return propertyInfo == null ? m : Expression.Property(exp, propertyInfo.Name); 41 } 42 43 protected override Expression VisitBinary(BinaryExpression b) 44 { 45 Expression left = this .Visit(b.Left); 46 Expression right = Calc(b.Right); // 对二元运算符右边的表达式进行求值 47 Expression conversion = this .Visit(b.Conversion); 48 if (b.NodeType == ExpressionType.Coalesce && b.Conversion != null ) 49 return Expression.Coalesce(left, right, conversion as LambdaExpression); 50 else 51 return Expression.MakeBinary(b.NodeType, left, right, b.IsLiftedToNull, b.Method); 52 } 53 54 protected override ReadOnlyCollection < Expression > VisitExpressionList( 55 ReadOnlyCollection < Expression > original) 56 { 57 if (original == null || original.Count == 0 ) 58 return original; 59 60 // 对参数进行求值运算 61 List < Expression > list = new List < Expression > (); 62 for ( int i = 0 , n = original.Count; i < n; i ++ ) 63 { 64 list.Add(Calc(original[i])); // 对调用函数的输入参数进行求值 65 } 66 return list.AsReadOnly(); 67 } 68 69 private static Expression Calc(Expression e) 70 { 71 LambdaExpression lambda = Expression.Lambda(e); 72 return Expression.Constant(lambda.Compile().DynamicInvoke( null ), e.Type); 73 } 74 }
最后,上代码:
博客园的文件貌似有缓存,删掉重新上传,总是覆盖不了,只好单独上传了
本文转自Silent Void博客园博客,原文链接:http://www.cnblogs.com/happyhippy/archive/2010/01/28/1658410.html,如需转载请自行联系原作者