Создайте выражение для другого с другими аргументами [дубликат]

avatar
Sassa
8 апреля 2018 в 02:17
76
1
0

Можно ли создать Expression, который получает тип X от Expression, который получает тип Y, если X содержит <5396840984<520925>6 внутри него?

Например, у меня есть такие типы:

public class Y {
    public int Something { get; set; }
}

public class X {
    public Y Y { get; set; }
}

Допустим, у меня есть эта функция, которая возвращает Y Expression:

private static Expression<Func<Y, bool>> ExprY(Y y2)
{
    return y1 => y1.Something == y2.Something;
}

Как видите, я могу послать этой функции объект Y, и она вернет Expression<Func<Y, bool>>.

.

Теперь, допустим, я хочу создать тот же самый Expression, но из X, таким образом я могу написать это:

private static Expression<Func<X, bool>> ExprX(X x2)
{
    return x1 => x1.Y.Something == x2.Y.Something;
}

Это работает нормально, но я дублирую код, так как одна и та же логика для сравнения Something находится внутри обеих функций, поэтому вопрос в том, как переписать ExprX, чтобы каким-то образом использовать ExprY внутри себя, поэтому мне не нужно повторить сравнение Something?

Что-то вроде этого:

public static Expression<Func<X, bool>> ExprX(X x2)
{
    return x1 => ExprY(x2.Y)(x1.Y);
}

ПС. это предназначено для кода, запускаемого Entity Framework 6 в предложении .Where, поэтому ответ должен работать с этой платформой.

Источник

Ответы (1)

avatar
Cheng Chen
8 апреля 2018 в 03:00
2

Вы можете использовать ExpressionVisitor, чтобы заменить все Y в выражении на x.Y и построить новое выражение.

class ReplaceParameterExpressionVisitor : ExpressionVisitor
{
    private readonly Expression _target, _replacement;

    public ReplaceParameterExpressionVisitor(ParameterExpression target, Expression replacement)
    {
        _target = target;
        _replacement = replacement;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return node.Equals(_target) ? _replacement : node;
    }
}

Посетитель очень прост, найдите цель ParameterExpression и замените ее выражением _replacement.

public static class ExpressionExtensions
{
    public static Expression<Func<T, R>> ReplaceParameter<T, R, U>(this Expression<Func<U, R>> origin, Expression<Func<T, U>> accessor)
    {
        var originalParameter = origin.Parameters.Single();
        var replacement = accessor.Body;
        var visitor = new ReplaceParameterExpressionVisitor(originalParameter, replacement);
        var newBody = visitor.Visit(origin.Body);
        return Expression.Lambda<Func<T, R>>(newBody, accessor.Parameters.Single());
    }
}

Приведенный выше метод расширения заменит параметры в теле с помощью посетителя, получит новое тело и построит новое лямбда-выражение с новым параметром.

class User
{
    public string Name { get; set; }
}

Expression<Func<string, bool>> exp = s => s.Equals("Jack");
Expression<Func<User, string>> nameAccessor = u => u.Name;
var newExp = exp.ReplaceParameter(nameAccessor);   // u => u.Name.Equals("Jack")