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