1// Generic repository interface
2public interface IRepository<T, TKey> where T : Entity<TKey>
3{
4 Task<T?> GetByIdAsync(TKey id, CancellationToken ct = default);
5 Task<IReadOnlyList<T>> GetAllAsync(CancellationToken ct = default);
6 Task AddAsync(T entity, CancellationToken ct = default);
7 Task UpdateAsync(T entity, CancellationToken ct = default);
8 Task DeleteAsync(TKey id, CancellationToken ct = default);
9}
10
11// EF Core implementation
12public class EfRepository<T, TKey>(AppDbContext db)
13 : IRepository<T, TKey> where T : Entity<TKey>
14{
15 private readonly DbSet<T> _set = db.Set<T>();
16
17 public Task<T?> GetByIdAsync(TKey id, CancellationToken ct)
18 => _set.FindAsync(new object?[] { id }, ct).AsTask();
19
20 public Task<IReadOnlyList<T>> GetAllAsync(CancellationToken ct)
21 => _set.AsNoTracking().ToListAsync(ct)
22 .ContinueWith(t => (IReadOnlyList<T>)t.Result);
23
24 public async Task AddAsync(T entity, CancellationToken ct)
25 => await _set.AddAsync(entity, ct);
26}
27
28// Unit of Work — groups multiple repositories into one transaction
29public class UnitOfWork(AppDbContext db) : IUnitOfWork
30{
31 public IRepository<Order, Guid> Orders { get; } = new EfRepository<Order, Guid>(db);
32 public IRepository<Product, int> Products { get; } = new EfRepository<Product, int>(db);
33
34 public Task<int> CommitAsync(CancellationToken ct = default)
35 => db.SaveChangesAsync(ct); // One transaction for all repos
36}
37
38// Usage in application service
39public class OrderService(IUnitOfWork uow)
40{
41 public async Task PlaceOrderAsync(Guid customerId, int productId)
42 {
43 var product = await uow.Products.GetByIdAsync(productId);
44 var order = new Order(customerId, product!);
45 await uow.Orders.AddAsync(order);
46 await uow.CommitAsync(); // Saves both in one transaction
47 }
48}