Я видел много проектов, у которых есть репозитории, которые возвращают экземпляры IQueryable
. Это позволяет выполнять дополнительные фильтры и сортировку IQueryable
по другому коду, что переводится в другой генерируемый SQL. Мне любопытно, откуда взялся этот паттерн и хорошая ли это идея.
Больше всего меня беспокоит то, что IQueryable
обещание попасть в базу данных через некоторое время, когда она будет перечислена. Это означает, что ошибка будет выброшена за пределы хранилища. Это может означать, что исключение Entity Framework выдается на другом уровне приложения.
В прошлом я также сталкивался с проблемами с множественными активными наборами результатов (MARS) (особенно при использовании транзакций), и этот подход звучит так, как если бы это происходило чаще.
Я всегда вызывал AsEnumerable
или ToArray
в конце каждого из своих выражений LINQ, чтобы убедиться, что база данных попадет перед выходом из кода репозитория.
Мне интересно, может ли возврат IQueryable
быть полезным в качестве строительного блока для уровня данных. Я видел довольно экстравагантный код с одним репозиторием, вызывающим другой репозиторий для создания еще большего IQueryable
.
источник
where
условие к отложенномуIQueryable
, вам нужно только отправить эти данные по проводной связи, а не весь набор результатов.Ответы:
Возвращение IQueryable определенно предоставит потребителям хранилища больше гибкости. Это возлагает ответственность за сужение результатов на клиента, что, естественно, может быть как выгодой, так и опорой.
С другой стороны, вам не нужно создавать тонны методов репозитория (по крайней мере, на этом уровне) - GetAllActiveItems, GetAllNonActiveItems и т. Д. - для получения нужных данных. Опять же, в зависимости от ваших предпочтений, это может быть хорошо или плохо. Вы будете (/ должны) необходимо определить поведенческие контракты , которые ваши реализации прилипают, но когда это идет от вас.
Таким образом, вы можете поместить логику поиска извне вне хранилища и разрешить ее использование по желанию пользователя. Таким образом, использование IQueryable дает наибольшую гибкость и обеспечивает эффективные запросы, а не фильтрацию в памяти и т. Д., И может снизить необходимость использования множества специальных методов извлечения данных.
С другой стороны, теперь вы дали своим пользователям дробовик. Они могут делать то, что вы, возможно, не имели в виду (чрезмерное использование .include (), выполнение тяжелых тяжелых запросов и фильтрацию в памяти в соответствующих реализациях и т. Д.), Что в основном обойдёт стороной многоуровневый и поведенческий контроль, потому что вы дали полный доступ.
Таким образом, в зависимости от команды, их опыта, размера приложения, общего уровня и архитектуры ... это зависит
источник
Expression<Func<Foo,bool>>
свой метод. Эти параметры могут быть переданыWhere
методу напрямую, что позволяет потребителю выбирать свой фильтр без необходимости прямого доступа к IQueryable <Foo>.Предоставление IQueryable публичным интерфейсам не является хорошей практикой. Причина фундаментальна: вы не можете обеспечить реализацию IQueryable, как об этом говорят.
Открытый интерфейс - это договор между провайдером и клиентами. И в большинстве случаев ожидается полная реализация. IQueryable это чит:
Шаблон репозитория дает нам четкое представление по слоям и, самое главное, независимость реализации. Таким образом, мы можем изменить в будущем источник данных.
Вопрос « Должна ли быть возможность замены источника данных? ».
Если завтра часть данных может быть перемещена в службы SAP, базу данных NoSQL или просто в текстовые файлы, можем ли мы гарантировать правильную реализацию интерфейса IQueryable?
( больше хороших моментов о том, почему это плохая практика)
источник
Реально, у вас есть три варианта, если вы хотите отсроченное выполнение:
IQueryable
.GetCustomersInAlaskaWithChildren
ниже)IQueryable
внутренне.Я предпочитаю третий (хотя я помогал коллегам также реализовать второй), но, очевидно, здесь есть некоторые настройки и обслуживание. (Т4 на помощь!)
Изменить : Чтобы устранить путаницу вокруг того, о чем я говорю, рассмотрите следующий пример, украденный из IQueryable Может убить вашу собаку, украсть вашу жену, убить вашу волю к жизни и т. Д.
В сценарии, где вы бы выставили что-то вроде этого:
API, о котором я говорю, позволил бы вам предоставить метод, который позволял бы вам
GetCustomersInAlaskaWithChildren
гибко выражать (или любую другую комбинацию критериев), а репо выполнял бы его как IQueryable и возвращал бы вам результаты. Но важно то, что он работает внутри уровня хранилища, поэтому он все еще использует преимущества отложенного выполнения. Как только вы вернете результаты, вы все равно сможете привязать к ним объекты, которые вам нравятся.Другое преимущество такого подхода состоит в том, что, поскольку они являются классами POCO, этот репозиторий может находиться за сетевым или WCF-сервисом; он может быть подвержен AJAX или другим абонентам, которые не знают в первую очередь о LINQ.
источник
IQueryable
не подвергаяIQueryable
себя . Как вы собираетесь это сделать со встроенным API?На самом деле есть только один законный ответ: это зависит от того, как хранилище будет использоваться.
В одном крайнем случае ваш репозиторий представляет собой очень тонкую оболочку вокруг DBContext, поэтому вы можете внедрить тестируемость в приложение, управляемое базой данных. На самом деле нет никаких реальных ожиданий, что это может быть использовано автономно без поддержки дружественной БД LINQ, потому что вашему приложению CRUD это никогда не понадобится. Тогда конечно, почему бы не использовать IQueryable? Я бы, вероятно, предпочел бы IEnumarable, поскольку вы получаете большинство преимуществ [читай: отложенное выполнение], и он не выглядит таким грязным.
Если вы не сидите в таком экстремальном состоянии, я бы изо всех сил старался воспользоваться духом шаблона репозитория и вернуть соответствующие материализованные коллекции, которые не имеют никакого отношения к базовой связи с базой данных.
источник
IEnumerable
, важно отметить, что((IEnumerable<T>)query).LastOrDefault()
он сильно отличается отquery.LastOrDefault()
(предполагается, чтоquery
этоIQueryable
). Таким образом, имеет смысл возвращаться,IEnumerable
если вы все равно собираетесь перебирать все элементы.НЕТ, если вы хотите провести тестирование на основе разработки / модульного тестирования, потому что нет простого способа создать фиктивный репозиторий для тестирования вашего businesslogic (= репозитория-потребителя) в изоляции от базы данных или другого поставщика IQueryable.
источник
С точки зрения чистой архитектуры IQueryable - это утечка абстракций. Как правило, он предоставляет пользователю слишком много энергии. При этом есть места, где это имеет смысл. Если вы используете OData, IQueryable делает чрезвычайно простым предоставление конечной точки, которая легко фильтруется, сортируется, группируется ... и т. Д. Я на самом деле предпочитаю создавать DTO, на которые отображаются мои сущности, а IQueryable возвращает DTO. Таким образом, я предварительно фильтрую свои данные и действительно использую только метод IQueryable, чтобы разрешить клиенту настраивать результаты.
источник
Я тоже столкнулся с таким выбором.
Итак, подведем итоги положительных и отрицательных сторон:
Положительных:
Отрицательных:
Я бы рекомендовал не использовать этот подход. Вместо этого рассмотрите возможность создания нескольких спецификаций и реализации методов репозитория, которые могут запрашивать базу данных, используя их. Шаблон спецификации - отличный способ инкапсулировать набор условий.
источник
Я думаю, что показ IQueryable в вашем хранилище вполне приемлем на начальных этапах разработки. Существуют инструменты пользовательского интерфейса, которые могут работать с IQueryable напрямую и обрабатывать сортировку, фильтрацию, группировку и т. Д.
При этом, я думаю, что просто разоблачить crud в хранилище и назвать его днем, безответственно. Наличие полезных операций и помощников по запросам для общих запросов позволяет централизовать логику для запроса, одновременно предоставляя потребителям хранилища меньшую поверхность интерфейса.
Для первых нескольких итераций проекта публичное раскрытие IQueryable в репозитории обеспечивает более быстрый рост. Перед первым полным выпуском я бы порекомендовал сделать операцию запроса частной или защищенной, а подстановочные запросы под одной крышей.
источник
То, что я видел, сделано (и то, что я сам реализую):
Это позволяет вам гибко выполнять
IQueryable.Where(a => a.SomeFilter)
запросы к репо,concreteRepo.GetAll(a => a.SomeFilter)
не подвергая риску бизнес LINQy.источник