Правильная реализация шаблона единицы работы

Я хочу реализовать Unit of Work для своего проекта интернет-магазина, но я не понимаю и не могу найти, как сделать так, чтобы репозитории были универсальными, и каждый раз, когда я хочу создать новую модель, мой универсальный репозиторий не нарушает интерфейс. принцип сегрегации. Если, например, я создаю новую модель и мне не нужна реализация обновления, она все равно будет добавлена. Я знаю, что могу выполнять все вызовы репо в сервисах, а затем сохранять изменения только в UnitOfWork, а также, если я не создам общее репо, я нарушу принцип открытости и закрытости. Здесь общий репо

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected readonly ApplicationDbContext _context;
    private readonly DbSet<TEntity> _dbSet;
    public Repository(ApplicationDbContext context)
    {
        _context = context;
        _dbSet = context.Set<TEntity>();
    }
    public async Task<TEntity> GetByIdAsync(int id)
    {
        return await _dbSet.FindAsync(id);
    }
    public async Task<IEnumerable<TEntity>> GetAllAsync()
    {
        return await _dbSet.ToListAsync();
    }
    public async Task AddAsync(TEntity entity)
    {
        await _dbSet.AddAsync(entity);
    }
    public void Update(TEntity entity)
    {
        _dbSet.Update(entity);
    }
    public void Remove(TEntity entity)
    {
        _dbSet.Remove(entity);
    }
}

Здесь единица работы:

public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationDbContext _context;
    private readonly Dictionary<Type, object> _repositories = new Dictionary<Type, object>();

    public UnitOfWork(ApplicationDbContext context)
    {
        _context = context;
    }
    public IRepository<TEntity> Repository<TEntity>() where TEntity : class
    {
        if (_repositories.ContainsKey(typeof(TEntity)))
            return (IRepository<TEntity>)_repositories[typeof(TEntity)];

        var repository = new Repository<TEntity>(_context);
        _repositories.Add(typeof(TEntity), repository);
        return repository;
    }
    public async Task<int> CompleteAsync()
    {
        return await _context.SaveChangesAsync();
    }
    public void Dispose()
    {
        _context.Dispose();
    }
}

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

Hina Khuman 15.08.2024 17:43

Вы просто создаете оболочку вокруг контекста EF. EF уже является UOW. Если вам нужна гибкость при получении, просто верните IQueryable<>.

GH DevOps 15.08.2024 17:45

Пакет Microsoft.EntityFrameworkCore — это абстракция над базами данных, DbContext — это уже UoW, а DbSet — это уже репозиторий. Зачем вам еще одна абстракция?

Svyatoslav Danyliv 15.08.2024 17:57

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

Nazar 15.08.2024 18:06
DbContext реализует UoW до того, как SaveChanges модификации будут собраны в ChangeTracker DbContext. В любом случае, приятного чтения Является ли Repository антипаттерном?
Svyatoslav Danyliv 15.08.2024 19:27
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Не используйте универсальный репозиторий поверх EF. Это антишаблон, и он занимает первое место среди худших вещей, которые вы можете сделать с помощью EF. EF уже предоставляет общий репозиторий в форме DbSet<TEntity>, а DbContext уже служит единицей работы. Сила EF заключается в его способности использовать IQueryable для облегчения проецирования и работы с совокупными структурами корневых сущностей посредством свойств навигации.

Ваша реализация уже предлагает только асинхронные методы чтения данных, что произойдет, если вам понадобится синхронный? async — это не волшебное ключевое слово «идти быстрее». На самом деле это немного медленнее. Это дает преимущество, когда в противном случае операции могут заблокировать что-то вроде контроллеров ASP.Net, которые могли бы отвечать на другие запросы, а не ждать длинного запроса, но если вы хотите получить что-то простое и быстрое, синхронный занимает меньший объем.

Такие методы, как:

public async Task<IEnumerable<TEntity>> GetAllAsync()
{
    return await _dbSet.ToListAsync();
}

... вы НИКОГДА не захотите видеть этот метод, вызываемый в рабочем коде. Как потребитель сортирует результаты? Как они фильтруют результаты? Что, если им нужен только подсчет? Что, если им нужно всего несколько полей от сущности? Что, если им нужна сущность, но вам также нужны некоторые связанные с ней данные? (Жаркая загрузка) Этот метод каждый раз извлекает все строки в память. 10 миллионов строк? все в памяти. Хотите, чтобы они были отсортированы или отфильтрованы? сортируются и фильтруются постфактум в памяти, а не позволяют базе данных, которая оптимизирована для этого, делать это. Конечно, вы можете передавать параметры, стеки и стеки необязательных параметров для сортировки, фильтрации, разбиения на страницы и т. д., а также сложные параметры, такие как Expression<Func<TEntity,bool>> filters или IEnumerable<(string, bool)> sortBy, которые сложны и бесполезны, и все это для того, чтобы попытаться абстрагировать EF-измы от вашего потребляющего кода. Но это не так. Выражения по-прежнему должны соответствовать правилам EF, чтобы их можно было преобразовать в SQL. Они не могут вызывать функции C#, использовать несопоставленные столбцы и т. д.

Хорошо, что вы знакомы с SOLID и другими принципами проектирования программного обеспечения. Когда дело доходит до шаблонов и принципов программного обеспечения, ранжирование важности решений будет примерно таким:

ПОЦЕЛУЙ (будь простым) > ТВЕРДЫЙ (Единая ответственность > Инверсия зависимостей > Замена Лискова > Разделение интерфейса > Открытое закрытие) >>> СУХОЙ (Не повторяйся)

Моя догадка, когда я вижу попытки относиться к принципам и, например, к Евангелию, заключается в том, что что-то вроде DRY ставится на пьедестал как самая важная цель, тогда как цель SOLID в конечном итоге состоит в том, чтобы удовлетворить DRY. DRY — отличное решение для рефакторинга, а не для проектирования программного обеспечения. Сначала спроектируйте и реализуйте самое простое, учитывая принципы SOLID. Если у вас есть заложенные, работающие и проверенные возможности, ТОГДА выполните рефакторинг, настройку и ужесточение. Изменение порядка и слишком усердное размышление о том, как должен выглядеть «идеальный» код, приводит к преждевременной оптимизации и чаще всего приводит к худшим решениям, которые являются более жесткими, трудными для понимания, менее производительными и не достигают самой важной цели программного обеспечения. удовлетворение ожиданий и требований пользователей. (Точность, производительность и стоимость)

Другие вопросы по теме