Мне нужно сделать некоторую фильтрацию на ObjectSet, чтобы получить нужные мне объекты:
query = this.ObjectSet.Where(x => x.TypeId == 3); // this is just an example;
Позже в коде (и перед запуском отложенного выполнения) я снова фильтрую запрос следующим образом:
query = query.Where(<another lambda here ...>);
Это хорошо работает до сих пор.
Вот моя проблема:
Объекты содержат свойство DateFrom и свойство DateTo, которые являются типами DataTime. Они представляют собой период времени.
Мне нужно отфильтровать объекты, чтобы получить только те, которые являются частью коллекции периодов времени. Периоды в коллекции не обязательно смежные, поэтому логика для восстановления объектов выглядит следующим образом:
entities.Where(x => x.DateFrom >= Period1.DateFrom and x.DateTo <= Period1.DateTo)
||
entities.Where(x => x.DateFrom >= Period2.DateFrom and x.DateTo <= Period2.DateTo)
||
... и снова и снова для всех периодов в коллекции.
Я пробовал это:
foreach (var ratePeriod in ratePeriods)
{
var period = ratePeriod;
query = query.Where(de =>
de.Date >= period.DateFrom && de.Date <= period.DateTo);
}
Но как только я запускаю отложенное выполнение, он переводит это в SQL так же, как я хочу (один фильтр для каждого из периодов времени для такого количества периодов, которые есть в коллекции), НО это означает вместо И сравнения OR сравнения, которая не возвращает никаких сущностей вообще, поскольку сущность не может быть частью более чем одного периода времени, очевидно.
Мне нужно создать какой-то динамический linq для агрегирования фильтров периодов.
Обновление
На основе ответа на шляпу я добавил следующего участника:
private Expression<Func<T, bool>> CombineWithOr<T>(Expression<Func<T, bool>> firstExpression, Expression<Func<T, bool>> secondExpression)
{
// Create a parameter to use for both of the expression bodies.
var parameter = Expression.Parameter(typeof(T), "x");
// Invoke each expression with the new parameter, and combine the expression bodies with OR.
var resultBody = Expression.Or(Expression.Invoke(firstExpression, parameter), Expression.Invoke(secondExpression, parameter));
// Combine the parameter with the resulting expression body to create a new lambda expression.
return Expression.Lambda<Func<T, bool>>(resultBody, parameter);
}
Объявлено новое выражение CombineWithOr:
Expression<Func<DocumentEntry, bool>> resultExpression = n => false;
И использовал его в моей итерации коллекции периода следующим образом:
foreach (var ratePeriod in ratePeriods)
{
var period = ratePeriod;
Expression<Func<DocumentEntry, bool>> expression = de => de.Date >= period.DateFrom && de.Date <= period.DateTo;
resultExpression = this.CombineWithOr(resultExpression, expression);
}
var documentEntries = query.Where(resultExpression.Compile()).ToList();
Я посмотрел на полученный SQL, и это похоже на то, что выражение не имеет никакого эффекта. Полученный SQL возвращает ранее запрограммированные фильтры, но не комбинированные фильтры. Почему?
Обновление 2
Я хотел дать предложение feO2x попробовать, поэтому я переписал свой запрос фильтра следующим образом:
query = query.AsEnumerable()
.Where(de => ratePeriods
.Any(rp => rp.DateFrom <= de.Date && rp.DateTo >= de.Date))
Как вы можете видеть, я добавил AsEnumerable()
, но компилятор дал мне ошибку, что он не может преобразовать IEnumerable обратно в IQueryable, поэтому я добавил ToQueryable()
в конце моего запроса:
query = query.AsEnumerable()
.Where(de => ratePeriods
.Any(rp => rp.DateFrom <= de.Date && rp.DateTo >= de.Date))
.ToQueryable();
Все работает отлично. Я могу скомпилировать код и запустить этот запрос. Однако это не соответствует моим потребностям.
При профилировании полученного SQL я вижу, что фильтрация не является частью SQL-запроса, поскольку она фильтрует даты в памяти во время процесса. Я предполагаю, что вы уже знаете об этом, и это то, что вы намеревались предложить.
Ваше предложение работает, НО, так как он извлекает все сущности из базы данных (и их тысячи и тысячи), прежде чем фильтровать их в памяти, он действительно медленно возвращает тот огромный сумму из базы данных.
Я действительно хочу отправить фильтрацию периода как часть результирующего SQL-запроса, поэтому он не вернет огромное количество объектов до окончания процесса фильтрации.