Добавление субпредиката к базовому выражению не работает, но тот же субпредикат включен как часть базового предиката building работает

#c# #.net

#c# #.net

Вопрос:

Этот подход включения субпредиката в базовое выражение работает:

 private void SomeCallerMethod()
{
    var predicate = GetBasePredicate(filterA:"123456");
}

private Expression<Func<Item, bool>> GetBasePredicate(string filterA)
{
    Expression<Func<PublicationItem, bool>> predicate = PublicationPredicateExtensions.GetRootExpression();
            
    if (!string.IsNullOrEmpty(filterA)
        predicate = predicate.And(PublicationPredicateExtensions.Get_FilterA_Expression(filterA));

    return predicate;
}

public static Expression<Func<Item, bool>> Get_FilterA_Expression(string filterA)
{
    return (p => !p.Meta.Contains(filterA));
}

 

Создание коллекции предикатов путем добавления субпредиката к базовому предикату не работает:

 basePredicate = GetBasePredicate();
basePredicate.And(p => !p.Meta.Contains("123456"));

private Expression<Func<Item, bool>> GetBasePredicate()
{
    Expression<Func<Item, bool>> predicate = PublicationPredicateExtensions.GetRootExpression();
    return predicate;
}

 

Комментарии:

1. Что означает «не работает»; что происходит? Попробуйте AndAlso вместо And

Ответ №1:

При объединении деревьев выражений вы не можете просто использовать And(p => !p.Meta.Contains("123456") (или даже AndAlso ) — вам нужно разрешить это второе выражение в терминах тех же параметров, которые использовались первым. Один из способов сделать это — использовать Expression.Invoke для вызова внутреннего лямбда-выражения, передавая параметр внешнего, однако: это не повсеместно поддерживается всеми реализациями LINQ. Более полезный подход состоит в том, чтобы полностью переписать второй запрос (заменив все p на любое исходное выражение параметра) и использовать тело напрямую; например:

 Expression<Func<string, bool>> a = s => s.StartsWith("a");
Expression<Func<string, bool>> b = t => t.EndsWith("b");
var both = AndAlso(a, b);
Console.WriteLine(both); // s => (s.StartsWith("a") AndAlso s.EndsWith("b"))

// ...
static Expression<Func<T, bool>> AndAlso<T>(Expression<Func<T, bool>> x, Expression<Func<T, bool>> y)
{
    // GIGO
    if (x is null) return y;
    if (y is null) return x;
    // combine, swapping y's parameter if it doesn't match x's
    ParameterExpression xp = x.Parameters.Single(), yp = y.Parameters.Single();
    var yBody = ReferenceEquals(xp, yp) ? y.Body : new SwapVisitor(yp, xp).Visit(y.Body);
    return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(x.Body, yBody), x.Parameters);
}
class SwapVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public SwapVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
        => ReferenceEquals(node, from) ? to : base.Visit(node);
}