Я прорепозиторій, хоча я відійшов від загальних шаблонів сховищ. Натомість я узгоджую свої сховища з функцією бізнесу, яку вони обслуговують. Репозиторії не спрямовані на те, щоб абстрагувати ORM, оскільки це не те, що я сподіваюся змінити, і в той же час я уникаю робити сховище занадто детальним. (Тобто CRUD) Натомість мої сховища виконують два-три ключові цілі:
- Пошук даних
- Створення даних
- Жорстке видалення
Для пошуку даних сховище завжди повертається IQueryable<TEntity>
. Для створення даних він повертає TEntity. Репозиторій обробляє мій базовий рівень фільтрації, такий як "активний" стан авторизації для систем, які використовують схеми м'якого видалення, та "поточний" стан для систем, що використовують історичні дані. Створення даних несе відповідальність лише за те, щоб потрібні посилання були вирішені та пов'язані, а також організація створена та готова до роботи.
Оновлення даних - це відповідальність бізнес-логіки, яка працює з відповідними суб'єктами (організаціями). Сюди можна віднести такі речі, як правила перевірки. Я не намагаюся інкапсулювати це методом сховища.
Видалення в більшості моїх систем є м'яким видаленням, щоб потрапити під оновлення даних. (IsActive = false) У випадку жорсткого видалення це буде одноклапник у сховищі.
Чому сховища? Тест-здатність. Звичайно, з DbContext можна знущатися, але простіше знущатися над класом, який повертаєтьсяIQueryable<TEntity>
. Вони також чудово грають з шаблоном UoW; я особисто використовую шаблон DbContextScope Мехдіме, щоб розширити одиницю роботи на потрібному рівні (тобто контролери в MVC) і дозволити моїм контролерам і допоміжним класам обслуговування використовувати сховища без необхідності передавати посилання на UoW / dbContext навколо. Використання IQueryable означає, що вам не потрібно багато методів обгортки у сховищі, і ваш код може оптимізувати спосіб використання даних. Наприклад, сховище не потребує викриття таких методів, як "Існує" або "Порахувати", або намагатися обернути об'єкти іншими 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 моєму глузливому сховищу просто потрібно повернути Списки населених об'єктів.
Що стосується загальних сховищ, то я здебільшого віддалився від них, тому що, коли ви опиняєтесь сховищем у таблиці, ваші контролери / служби в кінцевому підсумку мають посилання на кілька сховищ, щоб зробити одну ділову дію. У більшості випадків лише одне або два з цих сховищ насправді виконують операції запису (за умови правильного використання властивостей навігації), а решта підтримують ті, які читають. Я вважаю за краще мати щось на зразок OrdersRepository, який здатний читати і створювати замовлення, а також читати будь-які відповідні пошуки тощо (легкі об’єкти, продукти тощо) для довідки при створенні замовлення, ніж натискання на 5 або 6 різних сховищ. Це може порушувати пуристів DNRY, але мій аргумент для цього полягає в тому, що мета сховища - служити створенню замовлень, що включає відповідні посилання.Repository<Product>
щоб отримати продукти там, де для основи замовлення мені потрібна лише організація з кількома полями. Мій OrderRepository може мати .GetProducts()
метод повернення, IQueryable<ProductSummary>
який мені здається кращим, ніж той, Repository<Product>
що в кінцевому підсумку має кілька методів "Get", щоб спробувати обслуговувати різні області потреб додатків та / або якийсь складний вираз фільтрації для входу.
Я вибираю простіший код, який легко слідкувати, тестувати та налаштовувати. Це можна зловживати поганими способами, але я б скоріше мав щось, де зловживання легко помітити і виправити, ніж намагатися "заблокувати" код таким чином, щоб не можна було ними зловживати, не вдаватись до нього, а потім мати щось, що є кошмар, щоб насправді зробити те, що платить клієнт, щоб в кінцевому підсумку це зробити. :)