Использование шаблона репозитория для поддержки нескольких провайдеров - PullRequest
14 голосов
/ 19 мая 2011

Ну, не уверен, что это правильный заголовок, но в основном у меня было много проблем с использованием репозиториев в приложениях MVC таким образом, что вы можете заменить один набор репозиториев, реализуя другую технологию хранения данных, для другой.

Например, предположим, что я хочу использовать Entity Framework для своего приложения. Однако я также хочу, чтобы набор тестовых данных был реализован в жестко закодированных списках. Я хотел бы иметь набор интерфейсов (IUserRepository, IProductRepository и т. Д. - давайте пока не будем говорить о более универсальном IRepository ), который могут быть реализованы в обоих подходах. Затем, используя, скажем, инструмент внедрения зависимостей, такой как Ninject или Castle Windsor, я могу переключаться между провайдером платформы сущностей (обращаясь к фактической базе данных) и провайдером испытаний (обращаясь к спискам).

В двух словах, вот проблема:

- Если вы собираетесь использовать Entity Framework, вы хотите, чтобы ваши репозитории возвращали IQueryable .

- Если вы собираетесь использовать жестко закодированные списки, вы НЕ хотите, чтобы ваши репозитории возвращали IQueryable, потому что это сильно увеличивает накладные расходы, и, кроме того, Linq для сущностей значительно отличается от Linq для объектов, вызывая много головных болей в коде, который является общим для обоих провайдеров.

Другими словами, я обнаружил, что наилучший подход изолирует весь EF-зависимый код в репозиториях, так что сами репозитории возвращают IEnumerable или IList или что-то подобное - тогда и EF, и некоторые другие технологии могут использовать один и тот же хранилища. Таким образом, все IQueryable будут содержаться в репозиториях EF. Таким образом, вы можете использовать Linq to Entities с репозиториями EF и Linq to Objects с репозиториями Test.

Тем не менее, этот подход помещает огромное количество бизнес-логики в репозитории и приводит к значительному дублированию кода - логику необходимо дублировать в каждом из репозиториев, даже если реализации несколько отличаются.

Вся идея о хранилищах, так как этот слой очень тонкий и просто подключается к базе данных, затем теряется - хранилища являются «хранилищами» бизнес-логики, а также связности хранилищ данных. Вы не можете просто найти, сохранить, обновить и т. Д.

Мне не удалось устранить это несоответствие между необходимостью изолировать код, зависящий от поставщика, и наличием бизнес-логики в централизованном месте.

Есть идеи? Если бы кто-нибудь мог указать мне пример реализации, которая решает эту проблему, я был бы очень признателен. (Я много читал, но не могу найти ничего, что конкретно говорит об этих проблемах.)

UPDATE:

Полагаю, я начинаю чувствовать, что, вероятно, невозможно иметь репозитории, которые можно поменять местами для разных провайдеров, - например, если вы собираетесь использовать Entity Framework, вам просто нужно посвятить все свое приложение. к Entity Framework. Модульные тесты? Я борюсь с этим. До этого момента я практиковал создание отдельного хранилища с жестко закодированными данными и его использование для модульного тестирования, а также для тестирования самого приложения перед настройкой базы данных. Я думаю, что мне придется искать другое решение, возможно, какой-нибудь насмешливый инструмент.

Но тогда возникает вопрос, зачем использовать репозитории, и особенно зачем использовать интерфейсы репозитория. Я работаю над этим. Я думаю, что определение наилучшей практики потребует немного исследований.

1 Ответ

14 голосов
/ 19 мая 2011

Что я могу сказать?Добро пожаловать в клуб ...

Проблема, с которой вы столкнулись, была достигнута многими разработчиками, которые последовали за "бумом хранилищ" с EFv4.Да, это проблема, и проблема действительно сложна.Я обсуждал это несколько раз:

Отдельная тема - зачем использовать репозитории:

По сути, предложенный вами способ является решением, но вы действительно этого хотите?На мой взгляд, результатом является не репозиторий, а объект доступа к данным (DAO), который предоставляет множество методов доступа.Определение репозитория Martin Fowler :

Репозиторий является посредником между доменом и слоями отображения данных, действуя как коллекция объектов домена в памяти.Объекты клиента создают декларативные спецификации запросов и отправляют их в хранилище для удовлетворения.Объекты могут добавляться и удаляться из репозитория, как и из простой коллекции объектов, и код отображения, инкапсулированный репозиторием, будет выполнять соответствующие операции за кулисами.Концептуально репозиторий инкапсулирует набор объектов, сохраняемых в хранилище данных, и операции, выполняемые над ними, обеспечивая более объектно-ориентированное представление уровня персистентности.Репозиторий также поддерживает цель достижения чистого разделения и односторонней зависимости между доменом и слоями отображения данных.

Я считаю, что экспонирование IQueryable в 100 раз лучше, чем создание открытого интерфейса, аналогичногохранилища эпохи Хранимых процедур - один метод доступа для хранимой процедуры (фиксированный запрос).

Проблема может быть обобщена по правилу неплотная абстракция .IQueryable является абстракцией запроса к базе данных, но функции, предоставляемые IQueryable, зависят от поставщика.Другой поставщик = другой набор функций.

Что такое вывод?Вы хотите такую ​​архитектуру из-за тестирования?В таком случае начните использовать интеграционные тесты, как предложено в первых двух связанных ответах, потому что, на мой взгляд, это самый болезненный способ.Если вы используете предложенный подход, вы все равно должны использовать интеграционные тесты для проверки ваших репозиториев, скрывающих всю связанную с EF логику и запросы.

...