Применить фильтр внутреннего выражения к IQueryable

#c# #linq #lambda #expression #iqueryable

#c# #linq #лямбда — выражение #выражение #iqueryable #лямбда

Вопрос:

Предположим, у меня есть следующие объекты:

 public class Source { public string Name; }
public class Target { public string Name; }
public class Result 
{ 
   public Source SourceObj; 
   public Target TargetObj; 
} 
  

Теперь, получив IQueryable<Result> откуда-то, я хотел бы подготовить для него фильтр выражений, просто имея целевой фильтр в качестве выражения: Expression<Func<Target, bool>> filter . Сигнатура метода фильтра выглядит следующим образом:

 public Expression<Func<Result, bool>> Filter(IQueryable<Result> collection, Expression<Func<Target, bool>> targetFilter)
{ 
   in result expression: "in given collection select items where their property TargetObj satisfies targetFilter"
}
  

Любые предложения будут очень оценены. Спасибо

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

1. В чем здесь вопрос?

Ответ №1:

Я не совсем уверен, что правильно понял вашу цель, но вот то, что я думаю, вероятно, то, что вы хотите.

С помощью linq Select вы можете сопоставить элемент из вашей исходной коллекции. Итак, здесь мы сопоставляем результат с целью, а затем применяем ваш целевой фильтр.

 public IQueryable<Target> GetFilteredTargets(IQueryable<Result> collection, Expression<Func<Target, bool>> targetFilter)
{
    return collection.Select(result => result.Target).Where(targetFilter);
}
  

Ответ №2:

Если вам действительно нужно выражение в качестве возврата для вашего Filter метода, вам не нужно IQueryable<Result> collection .

 public Expression<Func<Result, bool>> Filter(Expression<Func<Target, bool>> targetFilter)
{
    Func<Target, bool> func = targetFilter.Compile();
    Expression<Func<Result, bool>> resultExpression = r => func(r.TargetObj);

    return resultExpression;
}
  

использование:

 var results = new List<Result>()
{
   new Result() { TargetObj = new Target() { Name = "A" }},
   new Result() { TargetObj = new Target() { Name = "B" }},
   new Result() { TargetObj = new Target() { Name = "C" }},
};

var expression = Filter(r => r.Name == "B");
var func = expression.Compile();

var filteredResults = results.Where(r => func(r));
  

Образец отфильтрованного результата здесь вернет цель с помощью B. Для меня было бы разумнее использовать Linq, где напрямую основано на моем понимании вашей цели.

РЕДАКТИРОВАТЬ: поскольку вы добавили новый фактор в свой вопрос, вот как вы можете добавить целевой фильтр к базовому фильтру.

 public Expression<Func<Result, bool>> Filter(Expression<Func<Result, bool>> baseFilter, Expression<Func<Target, bool>> targetFilter)
{
    Func<Result, bool> baseFunc = baseFilter.Compile();
    Func<Target, bool> targetFunc = targetFilter.Compile();
    Expression<Func<Result, bool>> resultExpression = r => baseFunc(r) amp;amp; targetFunc(r.TargetObj);

    return resultExpression;
}
  

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

1. Чтобы быть более понятным, я просто хочу немного исправить свой вопрос. Предположим, я создаю сложное выражение фильтра для запроса результатов. Вместо IQueryable<Result> collection моего метода Filter должен иметь параметр baseFilter и выглядеть public Expression<Func<Result, bool>> Filter(Expression<Func<Result, bool>> baseFilter, Expression<Func<Target, bool>> targetFilter) следующим образом. Цель этого метода — каким-то образом «объединить» эти два фильтра, сделав targetFilter «внутренним» фильтром базового фильтра. Спасибо

2. @TimurGinesin Я обновил свой ответ, чтобы показать, как вы можете комбинировать свои базовые и целевые выражения фильтра.