Combining two expressions (Expression>)

前端 未结 7 1725
无人共我
无人共我 2020-11-22 01:09

I have two expressions of type Expression> and I want to take to OR, AND or NOT of these and get a new expression of the same type

7条回答
  •  我寻月下人不归
    2020-11-22 01:57

    Well, you can use Expression.AndAlso / OrElse etc to combine logical expressions, but the problem is the parameters; are you working with the same ParameterExpression in expr1 and expr2? If so, it is easier:

    var body = Expression.AndAlso(expr1.Body, expr2.Body);
    var lambda = Expression.Lambda>(body, expr1.Parameters[0]);
    

    This also works well to negate a single operation:

    static Expression> Not(
        this Expression> expr)
    {
        return Expression.Lambda>(
            Expression.Not(expr.Body), expr.Parameters[0]);
    }
    

    Otherwise, depending on the LINQ provider, you might be able to combine them with 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;
    }
    

    Somewhere, I have got some code that re-writes an expression-tree replacing nodes to remove the need for Invoke, but it is quite lengthy (and I can't remember where I left it...)


    Generalized version that picks the simplest route:

    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);
    }
    

    Starting from .NET 4.0, there is the ExpressionVisitor class which allows you to build expressions that are EF safe.

        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);
            }
        }
    

提交回复
热议问题