Я сторонник репозитория, хотя я отошел от общих шаблонов репозитория. Вместо этого я привожу свои репозитории в соответствие с бизнес-функциями, которые они выполняют. Репозитории не нацелены на абстрагирование ORM, так как это не то, что я ожидаю изменить, и в то же время я избегаю делать репозиторий слишком гранулированным. (Т.е. CRUD) Вместо этого мои репозитории служат двум-трем ключевым целям:
- Поиск данных
- Создание данных
- Жесткое удаление
Для извлечения данных хранилище всегда возвращается IQueryable<TEntity>
. Для создания данных он возвращает TEntity. Репозиторий обрабатывает мою фильтрацию базового уровня, такую как «активное» состояние авторизации для систем, которые используют шаблоны мягкого удаления, и «текущее» состояние для систем, которые используют исторические данные. Создание данных отвечает только за то, чтобы необходимые ссылки были разрешены и связаны, а объект был настроен и готов к работе.
Обновление данных является обязанностью бизнес-логики, работающей с соответствующими объектами. Это может включать в себя такие вещи, как правила проверки. Я не пытаюсь инкапсулировать это в метод репозитория.
Удаление в большинстве моих систем происходит при мягком удалении, поэтому это может привести к обновлению данных. (IsActive = false). В случае аппаратного удаления это будет одна строка в репозитории.
Почему репозитории? Тест-способность. Конечно, DbContext можно смоделировать, но проще смоделировать класс, который возвращаетIQueryable<TEntity>
, Они также хорошо работают с шаблоном UoW, лично я использую шаблон Mehdime DbContextScope, чтобы охватить единицу работы на уровне, который я хочу (т.е. контроллеры в MVC), и позволить моим контроллерам и вспомогательным классам обслуживания использовать репозитории без необходимости передавать ссылки на UoW / dbContext вокруг. Использование IQueryable означает, что вам не нужно много методов-оболочек в репозитории, и ваш код может оптимизировать использование данных. Например, хранилищу не нужно предоставлять такие методы, как «Exists» или «Count», или пытаться обернуть сущности другими POCO в тех случаях, когда вам нужны подмножества данных. Им даже не нужно обрабатывать загружаемые опции для связанных данных, которые могут вам понадобиться или не понадобиться. Передав IQueryable, вызывающий код может:
.Any()
.Count()
.Include() // Generally avoided, instead I use .Select()
.Where()
.Select(x => new ViewModel or Anon. Type)
.Skip().Take()
.FirstOrDefault() / .SingleOrDefault() / .ToList()
Очень гибкий, и из тестового PoV мой смоделированный репозиторий просто должен возвращать списки заполненных объектов-сущностей.
Что касается общих репозиториев, я по большей части отошел от них, потому что, когда вы получаете репозиторий для каждой таблицы, ваши контроллеры / сервисы заканчивают ссылками на несколько репозиториев для выполнения одного бизнес-действия. В большинстве случаев только один или два из этих репозиториев фактически выполняют операции записи (при условии, что вы правильно используете свойства навигации), в то время как остальные поддерживают те, что с Reads. Я предпочел бы иметь что-то вроде OrdersRepository, которое способно считывать и создавать заказы, а также читать любые релевантные запросы и т. Д. (Облегченные объекты клиентов, продукты и т. Д.) Для справки при создании заказа, а не использовать 5 или 6 различных репозиториев. Это может нарушить пуристов DNRY, но я утверждаю, что цель репозитория - служить созданию заказов, которые включают соответствующие ссылки.Repository<Product>
чтобы получить продукты, для которых на основании заказа мне нужен только объект с несколькими полями. В моем OrderRepository может быть .GetProducts()
метод, возвращающий, IQueryable<ProductSummary>
который, на мой взгляд, более приятный, чем метод, который в Repository<Product>
итоге имеет несколько методов «Get», чтобы попытаться обслуживать различные области потребностей приложения, и / или некоторое сложное выражение фильтрации передачи.
Я выбираю более простой код, который легко отслеживать, тестировать и настраивать. Его можно использовать ненадлежащим образом, но я предпочел бы иметь что-то, где злоупотребления легко обнаружить и исправить, чем пытаться «заблокировать» код таким образом, чтобы его нельзя было злоупотребить, потерпеть неудачу и получить что-то, что кошмар, чтобы заставить фактически делать то, что платит клиент, чтобы сделать это в конце. :)