墨色风雨
你可以用Expression.AndAlso / OrElse等等来组合逻辑表达式,但问题在于参数;您是否使用相同的ParameterExpression在expr1和expr2中?如果是这样,则更容易: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:// 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开始。有ExpressionVistor类,它允许您构建EF安全的表达式。 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);
}
}
Qyouu
您可以使用Expression.AndAlso/OrElse组合逻辑表达式,但必须确保参数表达式是相同的。我在EF和预测生成器所以我做了自己的事而不诉诸于调用,所以我可以像这样使用:var filterC = filterA.And(filterb);我的预测生成器的源代码:public static class PredicateBuilder {
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> 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<Func<T, bool>>(body, p);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> 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<Func<T, bool>>(body, p);
} }以及替换lambda中的参数的实用程序类:internal class SubstExpressionVisitor : System.Linq.Expressions.ExpressionVisitor {
public Dictionary<Expression, Expression> subst = new Dictionary<Expression, Expression>();
protected override Expression VisitParameter(ParameterExpression node) {
Expression newValue;
if (subst.TryGetValue(node, out newValue)) {
return newValue;
}
return node;
}
}