我创建了一个看起来像这样的谓词:
p.Name.Contains("Saw")
private static Expression<Func<T,bool>> BuildContainspredicate<T>(string propertyName,string propertyValue) { PropertyInfo propertyInfo = typeof (T).GetProperty(propertyName); // listofProducts.Where(p => p.Contains(propertyValue)) ParameterExpression pe = Expression.Parameter(typeof(T),"p"); MemberExpression memberExpression = Expression.MakeMemberAccess(pe,propertyInfo); MethodInfo methodInfo = typeof (string).getmethod("Contains",new Type[] {typeof (string)}); ConstantExpression constantExpression = Expression.Constant(propertyValue,typeof(string)); // Predicate Body - p.Name.Contains("Saw") Expression call = Expression.Call(memberExpression,methodInfo,constantExpression); Expression<Func<T,bool>> lambda = Expression.Lambda<Func<T,bool>>(call,pe); return lambda; }
但我想将谓词更改为:
p.Name.ToLower().Contains("Saw")
我空白了.我知道我必须在定义MethodInfo的地方添加一些东西.
有没有人有建议?
解决方法
而不是手动构造整个表达式只是因为一个小小块是动态的,你可以使用常规lambda来定义所有实际上是静态的内容,然后只需替换那些不是的小块.
具体来说,您可以使用的一般策略是使用一个带有表示您的小动态位的参数的lambda,然后在常规lambda中使用它,然后用动态构造的表达式替换该参数的所有实例:
private static Expression<Func<T,bool>> BuildContainspredicate<T>( string propertyName,string propertyValue) { Expression<Func<string,bool>> e = s => s.ToLower().Contains(propertyValue); var parameter = Expression.Parameter(typeof(T)); var property = Expression.PropertyOrField(parameter,propertyName); var body = e.Body.Replace(e.Parameters[0],property); return Expression.Lambda<Func<T,bool>>(body,parameter); }
这不仅简化了您的原始代码,而且对此静态代码进行其他更改就像编辑任何常规的旧C#代码一样简单,而不是要求所有的表达式操作,以及随之而来的复杂性(以及静态类型的丢失) .
public static Expression Replace(this Expression expression,Expression searchEx,Expression replaceEx) { return new ReplaceVisitor(searchEx,replaceEx).Visit(expression); } internal class ReplaceVisitor : ExpressionVisitor { private readonly Expression from,to; public ReplaceVisitor(Expression from,Expression to) { this.from = from; this.to = to; } public override Expression Visit(Expression node) { return node == from ? to : base.Visit(node); } }
另一种让事情变得更高级的方法是编写一个Compose方法,让你轻松地编写表达式.从概念上讲,我们将有两个lambda,我们想要创建一个lambda来表示调用一个并将其结果传递给另一个,然后返回结果:
public static Expression<Func<TFirstParam,TResult>> Compose<TFirstParam,TIntermediate,TResult>( this Expression<Func<TFirstParam,TIntermediate>> first,Expression<Func<TIntermediate,TResult>> second) { var param = Expression.Parameter(typeof(TFirstParam),"param"); var newFirst = first.Body.Replace(first.Parameters[0],param); var newSecond = second.Body.Replace(second.Parameters[0],newFirst); return Expression.Lambda<Func<TFirstParam,TResult>>(newSecond,param); }
这或多或少使用了我们上面使用的相同策略,但它概括了它而不是特殊的表达式.
然后,我们在将各块放在一起之前有一个剩余的辅助方法;创建一个表示访问属性的方法的方法,该属性由属性的字符串名称定义:
public static Expression<Func<T,string>> MemberSelector<T>(string propertyName) { var param = Expression.Parameter(typeof(T)); var body = Expression.PropertyOrField(param,propertyName); return Expression.Lambda<Func<T,string>>(body,param); }
使用这两个辅助方法(不依赖于任何特定情况),我们现在可以构建我们想要的lambda而无需任何自定义构建的表达式操作:
private static Expression<Func<T,string propertyValue) { return MemberSelector<T>(propertyName) .Compose(prop => prop.ToLower().Contains(propertyValue)); }