我有两个类型的表达式,Expression<Func<T, bool>>我想对它们进行“或”,“或”或“非”运算,并得到一个相同类型的新表达式
Expression<Func<T, bool>>
Expression<Func<T, bool>> expr1; Expression<Func<T, bool>> expr2; ... //how to do this (the code below will obviously not work) Expression<Func<T, bool>> andExpression = expr AND expr2
好了,您可以使用Expression.AndAlso/ OrElseetc组合逻辑表达式,但是问题是参数;您ParameterExpression在expr1和expr2中使用相同的代码吗?如果是这样,则更容易:
Expression.AndAlso
OrElse
ParameterExpression
var body = Expression.AndAlso(expr1.Body, expr2.Body); var lambda = Expression.Lambda<Func<T,bool>>(body, expr1.Parameters[0]);
这也可以很好地否定单个操作:
static Expression<Func<T, bool>> Not<T>( this Expression<Func<T, bool>> expr) { return Expression.Lambda<Func<T, bool>>( Expression.Not(expr.Body), expr.Parameters[0]); }
否则,根据LINQ提供程序,您可能可以将它们与Invoke:
Invoke
// OrElse is very similar... static Expression<Func<T, bool>> AndAlso<T>( this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right) { var param = Expression.Parameter(typeof(T), "x"); var body = Expression.AndAlso( Expression.Invoke(left, param), Expression.Invoke(right, param) ); var lambda = Expression.Lambda<Func<T, bool>>(body, param); return lambda; }
在某个地方,我有一些代码重新编写了一个替换节点的表达式树,以消除对的需要Invoke,但是它相当长(而且我不记得我把它留在哪里了……)。
选择最简单路线的通用版本:
static Expression<Func<T, bool>> AndAlso<T>( this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2) { // need to detect whether they use the same // parameter instance; if not, they need fixing ParameterExpression param = expr1.Parameters[0]; if (ReferenceEquals(param, expr2.Parameters[0])) { // simple version return Expression.Lambda<Func<T, bool>>( Expression.AndAlso(expr1.Body, expr2.Body), param); } // otherwise, keep expr1 "as is" and invoke expr2 return Expression.Lambda<Func<T, bool>>( Expression.AndAlso( expr1.Body, Expression.Invoke(expr2, param)), param); }
从.NET 4.0开始,有一个ExpressionVisitor类允许您构建EF安全的表达式。
ExpressionVisitor
public static Expression<Func<T, bool>> AndAlso<T>( this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2) { var parameter = Expression.Parameter(typeof (T)); var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter); var left = leftVisitor.Visit(expr1.Body); var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter); var right = rightVisitor.Visit(expr2.Body); return Expression.Lambda<Func<T, bool>>( Expression.AndAlso(left, right), parameter); } private class ReplaceExpressionVisitor : ExpressionVisitor { private readonly Expression _oldValue; private readonly Expression _newValue; public ReplaceExpressionVisitor(Expression oldValue, Expression newValue) { _oldValue = oldValue; _newValue = newValue; } public override Expression Visit(Expression node) { if (node == _oldValue) return _newValue; return base.Visit(node); } }