Содержание
- 2. Что такое репозиторий Абстракция от хранилища данных Интерфейс для добавления и удаления аналогичный коллекциям Поиск объектов
- 3. Реализация (Вон Вернон) public class HibernateCalendarEntryRepository implements CalendarEntryRepository { @Override public void add (CalendarEntry aCalendarEntry) {
- 4. UnitOfWork Управляет записью изменений для набора объектов, изменяемых в одной бизнес-транзакции
- 5. Опрос Кто считает, что эти паттерны актуальны (нужна своя реализация)? Кто считает, что нет?
- 6. Какие плюсы дает репозиторий: Изоляция доступа к данным в одном месте Работа с зависимыми сущностями через
- 7. Пример многие-ко-многим: модель public class Product { public ICollection ProductCategories { get; set; } } public
- 8. Удаление var product = await _uow.ProductRepository .GetWithCategoriesAsync(request.ProductId); if (product == null) throw new NotFoundException("Deleted product not
- 9. Удаление, чего хочется var product = await _uow.ProductRepository.Remove(request.ProductId); await _uow.SaveChangesAsync();
- 10. Корень агрегации: обновление var product = await _uow.ProductRepository .GetWithProductCategories(request.ProductId); // Delete not exists categories // Add
- 11. Удаление категорий //delete not existing in DTO categories foreach (var category in product.ProductCategories .Where(x => !newCategoryIds.Contains(x.CategoryId)))
- 12. Добавление новых категорий //new categories foreach (var categoryId in newCategoryIds.Except(currentCategoryIds)) { product.ProductCategories.Add(new ProductCategory { CategoryId =
- 13. Обновление, чего хочется var product = await _uow.ProductRepository.Update(request.Product); await _uow.SaveChangesAsync();
- 14. Абстракция ;) public interface IAppUnitOfWork : IUnitOfWork { AppDbContext Context { get; } }
- 15. Часто стремятся к такому public interface IUnitOfWork { IRepository UsersRepository { get; } IProductRepository ProductsRepository {
- 16. Но получается примерно так public interface IUnitOfWork { IRepository UsersRepository { get; } IProductRepository ProductsRepository {
- 17. Или вот так public interface IAvailableRepository : IRepository { Task > GetAllAvailableAsync(); IQueryable AllAvailable { get;
- 18. И скатывается к вот-такому public interface IUnitOfWork { IQueryable Users { get; } IQueryable Products {
- 19. Или даже такому public interface IUnitOfWork { DbSet Users { get; } DbSet Products { get;
- 20. Что имеем на практике Изоляция: CRUD код, который должен быть в репозитории, протекает в вызывающий код
- 21. Реализация Repository public class Repository where T : class { protected readonly DbSet DbSet; public Repository(AppDbContext
- 22. Еще одна реализация Repository public class Repository where T : class { protected readonly AppDbContext Context;
- 23. И так тоже бывает ☺ public abstract class AbstractRepository { protected readonly AppDbContext Context; protected AbstractRepository(AppDbContext
- 24. Взгляд из угла ORM A DbContext instance represents a combination of the Unit Of Work and
- 25. Плюсы реализации Repository поверх ORM
- 26. Репозиторий и ORM создает сложность Нужно писать дополнительный инфраструктурный уровень Нужно думать как сделать выборки с
- 27. Ну может хотя бы запросы? public class Product { public int Quantity { get; set; }
- 28. Репозиторий public class ProductRepository { public async Task > GetProductsByName(string name) { return await _context.Products .Where(x
- 29. Метод для инкапсуляции запроса protected IQueryable GetAvailableProducts() { return _context.Products.Where(x => x.IsAvailable && x.Quantity > 0);
- 30. Его использование в других запросах public async Task > GetProductsByName(string name) { return await GetAvailableProducts() .Where(x
- 31. Спецификация – классика public interface ISpecification { bool IsSatisfiedBy(T obj); }
- 32. Universal.Autofilter спецификация public class Product { public static Spec AvailableSpec = new Spec (x => x.IsAvailable
- 33. Все продукты public class ProductController { public async Task > GetAllProducts() { return await _context.Products .Where(Product.AvailableSpec)
- 34. Комбинация спецификаций public async Task > GetProductsByName(string name) { return await _context.Products .Where(Product.AvailableSpec && Product.ByNameSpec(name)) .ToListAsync();
- 35. LinqSpec – класс для каждой спецификации public abstract class Specification { public abstract Expression > ToExpression();
- 36. Реализация LinqSpec public class AvailableProductSpecification : Specification { public override Expression > ToExpression() { return x
- 37. Репозиторий Не нужен как абстракция источника данных Не нужен для избавления от дублирования в запросах
- 38. Чистая архитектура Дядя Боб: ORM – это инфраструктура, он которой нужно абстрагироваться. Ага, может быть новая
- 39. Получение списка через ORM internal class GetProductsQueryHandler { private readonly AppDbContext _context; public GetProductsQueryHandler(AppDbContext context) {
- 40. Получение списка через репозиторий public interface IUnitOfWork { IQueryable Products { get; } } public class
- 41. Хендлер с UnitOfWork вместо контекта internal class GetProductsQueryHandler { private readonly IUnitOfWork _uow; public GetProductsQueryHandler(IUnitOfWork uow)
- 42. Не все так просто ☹ using Microsoft.EntityFrameworkCore; internal class GetProductsQueryHandler { public async Task > HandleAsync(GetProductsQuery
- 43. Как переопределить Extension Method? Новый класс QueryableExecutor Service Locator
- 44. QuerableExecutor public interface IQueryableExecutor { Task > ToListAsync (IQueryable source); //SingleAsync //и остальные асинхронные методы по
- 45. Хендлер с IQueryableExecutor internal class GetProductsQueryHandler { public GetProductsQueryHandler(IUnitOfWork uow, IQueryableExecutor executor) { _uow = uow;
- 46. Не забываем про тестирование public class InMemoryQueryableExecutor : IQueryableExecutor { public async Task > ToListAsync (IQueryable
- 47. ServiceLocator и новые extension методы public static class QueryableExtensions { //Инициализация в конфигурации приложения или тесте
- 48. Хендлер без QuerableExecutor using Infrastructure.Interfaces; internal class GetProductsQueryHandler { private readonly IUnitOfWork _uof; public GetProductsQueryHandler(IUnitOfWork uof)
- 49. При миграции на другой ORM //EF 6 context.Blogs .Include(b => b.Posts.Select(p => p.Comments)) .ToList(); //EF Core
- 50. Миграция на другой ORM Надо учитывать API тех ORM, между которыми хотим заложить возможность перехода Надо
- 51. Итого UnitOfWork для абстракция ORM Не нужна, если не планируется переход на другой ORM Переход на
- 52. Как сделать DAL без Repository и UnitOfWork Сборка Infrastructure.Interfaces - интерфейс IDbContext Нет реализации OnModelCreating, зависимой
- 53. Infrastructure.Interfaces public interface IDbContext { DbSet Products { get; } Task SaveChangesAsync (CancellationToken cancellationToken = default);
- 54. Или два контекста для Read и CUD public interface IReadDbContext { DbSet Products { get; }
- 55. DataAccess.MsSql public class AppDbContext : IDbContext { DbSet Products { get; set; } protected override void
- 56. Если нужны EF.Functions (полнотекстовый поиск) Нужна ли поддержка нескольких баз одновременно? Да Делаем абстракции и свои
- 57. Если дублируется логика сохранения Инициализация ChangedAt+ChangedBy Перегрузка SaveChanges у контекста Пост-процессор в пайплайне обработки запроса (MediatR)
- 58. Модель данных public class AuditableEntity { public DateTime CreatedAt { get; set; } public int CreatedBy
- 59. Перегрузка SaveChanges у контекста public override Task SaveChangesAsync() { foreach (var entry in ChangeTracker.Entries ()) {
- 60. Интерфейс для отметки об изменениях public interface IChangeDataRequest { } public class ChangeEntityRequest : IReqiest, IChangeDataRequest
- 61. Хендлер обновления Entity (MediatR) public class ChangeEntityHandler : IRequestHandler { public async Task Handle(ChangeEntityRequest request) {
- 62. Пост-процессор IChangeDataRequest запросов public async Task Process(TRequest request, TResponse response) { _context.ChangeTracker.Entries ().ToList() .ForEach(x => {
- 63. Итого, отказ от Repository и UnitOfWork Избавляет от мук выбора: Использовать в контроллерах репозитории или сервисы
- 64. А что говорит Вон Вернон? От репозиториев будет польза только если у вас есть агрегаты Если
- 65. Мораль Не только пишем код по образцам дядек из умных книжек Но читаем комментарии к нему
- 66. Полезные ссылки по теме Что такое репозиторий Фаулер , Эванс Спецификация AutoFilter, не нужен отдельный класс
- 67. Холивар про репозиторий Нет – автор книги «EF Core in Action» Нет – автор EntityFramework.CommonTools Нет
- 68. Пример проекта с репозиториями и без них https://github.com/denis-tsv/DataAccessWithoutRepositoryAndUnitOfWork http://bit.ly/no-repository
- 69. Опрос Кто изменил мнение и считает, что он Repoisitory и UnitOfWork больше не нужны? А кто
- 70. Вопрос на подумать ☺ public class UpdateProductCommandHandler : IRequestHandler { protected override async Task Handle(UpdateProductCommand request)
- 72. Скачать презентацию