那么,您可以使用Expression.AndAlso
/ OrElse
etc来组合逻辑表达式,但问题是参数; 你ParameterExpression
在expr1和expr2中使用相同的工作吗?如果是这样,那就更容易了:
var body = Expression.AndAlso(expr1.Body, expr2.Body); var lambda = Expression.Lambda>(body, expr1.Parameters[0]);
这也适用于否定单个操作:
static Expression> Not ( this Expression > expr) { return Expression.Lambda >( Expression.Not(expr.Body), expr.Parameters[0]); }
否则,根据LINQ提供程序,您可以将它们与Invoke
以下内容组合:
// OrElse is very similar... static Expression> AndAlso ( this Expression > left, Expression > right) { var param = Expression.Parameter(typeof(T), "x"); var body = Expression.AndAlso( Expression.Invoke(left, param), Expression.Invoke(right, param) ); var lambda = Expression.Lambda >(body, param); return lambda; }
在某个地方,我有一些代码重写一个表达式树替换节点以消除需要Invoke
,但它是相当冗长的(我不记得我离开它的地方......)
选择最简单路线的通用版本:
static Expression> AndAlso ( this Expression > expr1, Expression > 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 >( Expression.AndAlso(expr1.Body, expr2.Body), param); } // otherwise, keep expr1 "as is" and invoke expr2 return Expression.Lambda >( Expression.AndAlso( expr1.Body, Expression.Invoke(expr2, param)), param); }
从.net 4.0开始.ExpressionVistor类允许您构建EF安全的表达式.
public static Expression> AndAlso ( this Expression > expr1, Expression > 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 >( 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); } }
您可以使用Expression.AndAlso/OrElse来组合逻辑表达式,但您必须确保ParameterExpressions是相同的.
我在使用EF和PredicateBuilder时遇到了麻烦,所以我自己创建了自己而没有使用Invoke,我可以像这样使用:
var filterC = filterA.And(filterb);
我的PredicateBuilder的源代码:
public static class PredicateBuilder { public static Expression> And (this Expression > a, Expression > b) { ParameterExpression p = a.Parameters[0]; SubstExpressionVisitor visitor = new SubstExpressionVisitor(); visitor.subst[b.Parameters[0]] = p; Expression body = Expression.AndAlso(a.Body, visitor.Visit(b.Body)); return Expression.Lambda >(body, p); } public static Expression > Or (this Expression > a, Expression > b) { ParameterExpression p = a.Parameters[0]; SubstExpressionVisitor visitor = new SubstExpressionVisitor(); visitor.subst[b.Parameters[0]] = p; Expression body = Expression.OrElse(a.Body, visitor.Visit(b.Body)); return Expression.Lambda >(body, p); } }
而实用程序类替换lambda中的参数:
internal class SubstExpressionVisitor : System.Linq.Expressions.ExpressionVisitor { public Dictionarysubst = new Dictionary (); protected override Expression VisitParameter(ParameterExpression node) { Expression newValue; if (subst.TryGetValue(node, out newValue)) { return newValue; } return node; } }
如果提供程序不支持Invoke并且您需要组合两个表达式,则可以使用ExpressionVisitor将第二个表达式中的参数替换为第一个表达式中的参数.
class ParameterUpdateVisitor : ExpressionVisitor { private ParameterExpression _oldParameter; private ParameterExpression _newParameter; public ParameterUpdateVisitor(ParameterExpression oldParameter, ParameterExpression newParameter) { _oldParameter = oldParameter; _newParameter = newParameter; } protected override Expression VisitParameter(ParameterExpression node) { if (object.ReferenceEquals(node, _oldParameter)) return _newParameter; return base.VisitParameter(node); } } static Expression> UpdateParameter ( Expression > expr, ParameterExpression newParameter) { var visitor = new ParameterUpdateVisitor(expr.Parameters[0], newParameter); var body = visitor.Visit(expr.Body); return Expression.Lambda >(body, newParameter); } [TestMethod] public void ExpressionText() { string text = "test"; Expression > expr1 = p => p.Item1.Contains(text); Expression > expr2 = q => q.Item2.Contains(text); Expression > expr3 = UpdateParameter(expr2, expr1.Parameters[0]); var expr4 = Expression.Lambda >( Expression.OrElse(expr1.Body, expr3.Body), expr1.Parameters[0]); var func = expr4.Compile(); Assert.IsTrue(func(new Coco { Item1 = "caca", Item2 = "test pipi" })); }