Замена имени параметра в теле выражения

Я пытаюсь динамически создавать выражения на основе объекта Specification.

Я создал класс ExpressionHelper, у которого есть личное выражение, например:

private Expression<Func<T, bool>> expression;

public ExpressionHelper()
{
    expression = (Expression<Func<T, bool>>)(a => true);
}

И затем несколько простых методов:

public void And(Expression<Func<T,bool>> exp);

Я борюсь с телом метода И. Я в основном хочу разорвать тело из exp, заменить все параметры на expression, а затем добавить его в конец тела expression как и AndAlso.

Я сделал это:

var newBody = Expression.And(expression.Body,exp.Body);

expression = expression.Update(newBody, expression.Parameters);

Но это заканчивается моим выражением, похожим на это:

{ a => e.IsActive && e.IsManaged }

Есть ли более простой способ сделать это? Или как я могу вырвать эти e и заменить их на?

Ответ 1

Самый простой способ - Expression.Invoke, например:

public static Expression<Func<T, bool>> AndAlso<T>(
    Expression<Func<T, bool>> x, Expression<Func<T, bool>> y)
{
    return Expression.Lambda<Func<T, bool>>(
        Expression.AndAlso(x.Body, Expression.Invoke(y, x.Parameters)),
        x.Parameters);
}

Это отлично работает для LINQ-to-Objects и LINQ-to-SQL, но не поддерживается EF. Для EF вам нужно будет использовать посетителя, чтобы печатать дерево, к сожалению.

Использование кода из Сочетание двух лямбда-выражений в С#

public static Expression<Func<T, bool>> AndAlso<T>(
    Expression<Func<T, bool>> x, Expression<Func<T, bool>> y)
{
    var newY = new ExpressionRewriter().Subst(y.Parameters[0], x.Parameters[0]).Inline().Apply(y.Body);

    return Expression.Lambda<Func<T, bool>>(
        Expression.AndAlso(x.Body, newY),
        x.Parameters);
}

Или в .NET 4.0, используя ExpressionVisitor:

class ParameterVisitor : ExpressionVisitor
{
    private readonly ReadOnlyCollection<ParameterExpression> from, to;
    public ParameterVisitor(
        ReadOnlyCollection<ParameterExpression> from,
        ReadOnlyCollection<ParameterExpression> to)
    {
        if(from == null) throw new ArgumentNullException("from");
        if(to == null) throw new ArgumentNullException("to");
        if(from.Count != to.Count) throw new InvalidOperationException(
             "Parameter lengths must match");
        this.from = from;
        this.to = to;
    }
    protected override Expression VisitParameter(ParameterExpression node)
    {
        for (int i = 0; i < from.Count; i++)
        {
            if (node == from[i]) return to[i];
        }
        return node;
    }
}
public static Expression<Func<T, bool>> AndAlso<T>(
      Expression<Func<T, bool>> x, Expression<Func<T, bool>> y)
{
    var newY = new ParameterVisitor(y.Parameters, x.Parameters)
              .VisitAndConvert(y.Body, "AndAlso");
    return Expression.Lambda<Func<T, bool>>(
        Expression.AndAlso(x.Body, newY),
        x.Parameters);
}