Ответ
Generic Repository — это реализация паттерна Repository, которая использует обобщённые типы (Generics) для создания единого, переиспользуемого класса, абстрагирующего операции доступа к данным (CRUD) для любой сущности предметной области.
Зачем это нужно? Чтобы избежать дублирования кода. Вместо создания отдельного репозитория для Product, Order, Customer и т.д., создаётся один общий репозиторий, работающий с типом TEntity.
Базовая реализация с Entity Framework Core:
public interface IRepository<T> where T : class
{
Task<T?> GetByIdAsync(int id);
Task<IEnumerable<T>> GetAllAsync();
Task AddAsync(T entity);
void Update(T entity);
void Delete(T entity);
Task SaveChangesAsync();
}
public class Repository<T> : IRepository<T> where T : class
{
protected readonly DbContext _context;
protected readonly DbSet<T> _dbSet;
public Repository(DbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
public async Task<T?> GetByIdAsync(int id) => await _dbSet.FindAsync(id);
public async Task<IEnumerable<T>> GetAllAsync() => await _dbSet.ToListAsync();
public async Task AddAsync(T entity) => await _dbSet.AddAsync(entity);
public void Update(T entity) => _dbSet.Update(entity);
public void Delete(T entity) => _dbSet.Remove(entity);
public async Task SaveChangesAsync() => await _context.SaveChangesAsync();
}
// Использование
public class ProductService
{
private readonly IRepository<Product> _productRepo;
public ProductService(IRepository<Product> productRepo) => _productRepo = productRepo;
public async Task<Product?> GetProduct(int id) => await _productRepo.GetByIdAsync(id);
}
Преимущества:
- Универсальность: Один класс для всех сущностей.
- Сокращение кода: Устраняет шаблонный код для базовых операций.
- Простота тестирования: Легко подменить моком через интерфейс.
Недостатки и предостережения:
- Ограниченность: Сложные запросы с джойнами, фильтрацией или агрегацией могут не вписаться в общий шаблон. Часто это решают, либо возвращая
IQueryable<T>из репозитория (что может нарушить инкапсуляцию), либо используя дополнительный паттерн Specification. - Антипаттерн? В простых CRUD-приложениях Generic Repository может быть излишним, добавляя ненужный слой абстракции поверх
DbSet. Его ценность возрастает в крупных проектах с чётким разделением слоёв и сложной бизнес-логикой.