Динамический LINQ имеет собственный язык выражений .Лямбда-выражения не начинаются с a =>
или ap =>
, есть нечто, называемое текущей областью действия, которое упрощает некоторые запросы, но в целом проблематично с доступом к параметрам внешнего уровня.Все запрашиваемые расширения определяют один параметр области действия, называемый it
, который может быть опущен.
Вскоре Dynamic LINQ не очень подходит для сложных запросов с вложенным лямбда-выражением, обращающимся к внешним параметрам лямбды.
Цель может быть достигнута относительно легко с помощью комбинации выражений времени компиляции и времени выполнения.Идея проста.
Сначала вы создаете лямбда-выражение времени компиляции с дополнительными параметрами, которые служат заполнителями.Затем вы заменяете заполнители фактическими выражениями, используя следующий простой посетитель выражений:
public static class ExpressionExtensions
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Source ? Target : base.VisitParameter(node);
}
}
Почти как string.Format
, но с выражениями.Затем вы можете использовать Expression.AndAlso
и Expression.OrElse
для изготовления деталей &&
и ||
.
С учетом сказанного, вот как это выглядит в вашем случае:
Expression<Func<Article, string, string, bool>> detailExpr = (a, var4, var5) =>
a.Properties.Any(ap => ap.ArticleCode == a.ArticleCode && ap.var4 == var4 && ap.var5 == var5);
var p_a = detailExpr.Parameters[0];
var p_var4 = detailExpr.Parameters[1];
var p_var5 = detailExpr.Parameters[2];
var body = dataFilter
.Select(filter => filter.Value
.Select(filterDetail => detailExpr.Body
.ReplaceParameter(p_var4, Expression.Constant(filter.Key))
.ReplaceParameter(p_var5, Expression.Constant(filterDetail.Key)))
.Aggregate(Expression.OrElse))
.Aggregate(Expression.AndAlso);
var predicate = Expression.Lambda<Func<Article, bool>>(body, p_a);
Затем используйте Where(predicate)
вместо вашего текущего Where(query)
.