Преамбула:
Мой основной вопрос очень похож на этот: Как я могу написать чистый репозиторий, не раскрывая IQueryable для остальной части моего приложения? , который остался без ответа. Я надеюсь, что если я подойду к проблеме по-другому и задам немного другой вопрос, я могу получить результат. Я повторю часть содержимого этого вопроса, чтобы читатели не читали его для контекста.
Проблема:
Я работаю с POCO-сущностями и Entity Framework 4. Я пытаюсь разрешить сложную специальную фильтрацию наборов сущностей на прикладном уровне, одновременно пытаясь избежать выставления IQueryable<T>
за границей моего хранилища. Это оставляет меня с некоторыми осложнениями.
Я не хочу создавать в хранилище один метод массивного фильтра, который принимает огромный список параметров, таких как:
IEnumerable GetFilteredCustomers(string nameFilter, string addressFilter, bool isActive, int customerId, ...)
Мало того, что это чрезвычайно громоздко в использовании, но смотреть на него очень некрасиво, особенно если это в основном куча нулей и т. Д. Он также не так удобен в обслуживании, как хотелось бы.
Я не хочу создавать огромный набор методов фильтрации в хранилище, таких как:
IEnumerable GetActiveCustomers()
IEnumerable GetCustomersByName()
Существует ряд проблем с этим подходом, включая необходимость в огромном списке методов, который увеличивается до n!
, где n - количество доступных условий фильтрации, если я хочу иметь возможность комбинировать их произвольным образом. (то есть всех активных клиентов с именем Джордж). Также очень трудно поддерживать.
Я не хочу создавать цепочечные методы (Fluent Interface), которые манипулируют IEnumerable<T>
, потому что в конечном счете это включает в себя возврат огромного набора результатов из базы данных и его фильтрацию в памяти, которая не является масштабируемым решением. .
Я не могу создать интерфейс Fluent, который манипулирует IQueryable<T>
, потому что, как я уже сказал, я не хочу показывать IQueryable<T>
мимо репозиториев.
Я бы хотел избежать простой перефразировки метода одиночного массивного фильтра, передавая объект, полный параметров, вместо большого списка параметров, хотя на данный момент это может быть наименее уродливым решением.
Идеи:
В конечном счете, я думаю, что идеальным решением было бы найти какой-то способ создать полный запрос, который не знает источника, и сохранить его в качестве параметра. Затем я мог бы передать это в хранилище, где источник известен, и применить запрос к источнику и вернуть результаты.
уточнить; в отличие от простого создания объекта параметров, как упомянуто выше, я хотел бы использовать необработанные запросы LINQ, но каким-то образом сохранить их в переменной и позже применить их к источнику данных. Я подозреваю, что тип возвращаемого значения должен быть известен заранее, но я прекрасно справлюсь с определением этого и заранее знаю его.
Чтобы рассмотреть это с другой стороны, рассмотрим следующее:
IQueryable<Customer> filteredCustomers = customerRepository.GetAll()
.Where(c => c.FirstName == "Dave")
.Where(c => c.IsActive == true)
.Where(c => c.HasAddress == true)
;
Я хочу упаковать три предложения Where в качестве объекта запроса, полностью отделенного от customerRepository.GetAll (), передать его в качестве параметра и применить его позже.