Я хочу реализовать 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();
}
}
Вы просто создаете оболочку вокруг контекста EF. EF уже является UOW. Если вам нужна гибкость при получении, просто верните IQueryable<>.
Пакет Microsoft.EntityFrameworkCore
— это абстракция над базами данных, DbContext
— это уже UoW, а DbSet
— это уже репозиторий. Зачем вам еще одна абстракция?
Я сейчас учусь, поэтому не уверен, просто видел, как многие гиды его используют. То есть вы рекомендуете не использовать единицы работы и взаимодействовать с моей базой данных, например, в сервисах? Я видел, что UoW помогает выполнить все транзакции перед сохранением и может помочь остановить процесс, если где-то произошла ошибка. Без него вы можете сохранить некоторые изменения до того, как часть ошибки начнет выполняться.
DbContext
реализует UoW до того, как SaveChanges
модификации будут собраны в ChangeTracker
DbContext. В любом случае, приятного чтения Является ли Repository антипаттерном?Не используйте универсальный репозиторий поверх 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. Если у вас есть заложенные, работающие и проверенные возможности, ТОГДА выполните рефакторинг, настройку и ужесточение. Изменение порядка и слишком усердное размышление о том, как должен выглядеть «идеальный» код, приводит к преждевременной оптимизации и чаще всего приводит к худшим решениям, которые являются более жесткими, трудными для понимания, менее производительными и не достигают самой важной цели программного обеспечения. удовлетворение ожиданий и требований пользователей. (Точность, производительность и стоимость)
Создайте общие репозитории для каждого метода, таким образом вы сможете наследовать только необходимые методы. Поэтому, если вам вообще не нужен метод Update, просто не наследуйте этот интерфейс.