IConfiguration vs IOptions NET
Synchronous and Asynchronous in .NET Core
Model Binding and Validation in ASP.NET Core
ControllerBase vs Controller in ASP.NET Core
ConfigureServices and Configure methods
IHostedService interface in .NET Core
ASP.NET Core request processing
| Repository pattern | NoSQL Integration (MongoDB, Redis) | |
🧩 Unit of Work Pattern in .NET Core |
The Unit of Work (UoW) pattern is a design pattern that coordinates the work of multiple repositories by treating a set of operations as a single transaction. It ensures that either all operations succeed or none of them are committed, maintaining data consistency.
public interface IProductRepository
{
Task<IEnumerable<Product>> GetAllAsync();
Task AddAsync(Product product);
}
public interface IOrderRepository
{
Task<IEnumerable<Order>> GetAllAsync();
Task AddAsync(Order order);
}
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context) => _context = context;
public async Task<IEnumerable<Product>> GetAllAsync() => await _context.Products.ToListAsync();
public async Task AddAsync(Product product) => await _context.Products.AddAsync(product);
}
public class OrderRepository : IOrderRepository
{
private readonly AppDbContext _context;
public OrderRepository(AppDbContext context) => _context = context;
public async Task<IEnumerable<Order>> GetAllAsync() => await _context.Orders.ToListAsync();
public async Task AddAsync(Order order) => await _context.Orders.AddAsync(order);
}
public interface IUnitOfWork : IDisposable
{
IProductRepository Products { get; }
IOrderRepository Orders { get; }
Task<int> CompleteAsync();
}
public class UnitOfWork : IUnitOfWork
{
private readonly AppDbContext _context;
public IProductRepository Products { get; }
public IOrderRepository Orders { get; }
public UnitOfWork(AppDbContext context, IProductRepository products, IOrderRepository orders)
{
_context = context;
Products = products;
Orders = orders;
}
public async Task<int> CompleteAsync() => await _context.SaveChangesAsync();
public void Dispose() => _context.Dispose();
}
public class OrderService
{
private readonly IUnitOfWork _unitOfWork;
public OrderService(IUnitOfWork unitOfWork) => _unitOfWork = unitOfWork;
public async Task PlaceOrder(Order order, Product product)
{
await _unitOfWork.Products.AddAsync(product);
await _unitOfWork.Orders.AddAsync(order);
await _unitOfWork.CompleteAsync(); // commits both in one transaction
}
}
DbContext already acts like a Unit of Work, so it can be redundant.DbContext.
The Unit of Work Pattern is a powerful way to manage transactions across
multiple repositories in .NET Core. While EF Core’s DbContext
already provides UoW-like behavior, implementing it explicitly can improve
clarity, maintainability, and testability in larger projects.
| Aspect | Repository Pattern | Unit of Work Pattern | Specification Pattern |
|---|---|---|---|
| Definition | Abstracts data access logic, providing a collection-like interface for entities. | Coordinates multiple repositories and ensures all changes are committed as a single transaction. | Encapsulates query logic into reusable, composable specifications. |
| Main Purpose | Decouple business logic from persistence logic. | Maintain consistency across multiple operations. | Centralize and reuse complex query logic. |
| When to Use | Anytime you want clean separation of concerns and testability. | When multiple repositories need to be updated in a single transaction. | When queries are complex, repeated, or need to be combined dynamically. |
| Advantages | Improves maintainability, testability, and readability. | Ensures atomic commits, reduces risk of partial updates. | Promotes DRY principle, improves readability of queries. |
| Disadvantages | May duplicate EF Core’s DbSet functionality. | EF Core’s DbContext already acts like a UoW, so can be redundant. | Extra abstraction layer, may be overkill for simple queries. |
| Example in .NET Core | _repository.Add(product); |
await _unitOfWork.CompleteAsync(); |
var spec = new ProductsByCategorySpec("Electronics"); |
- The Repository Pattern abstracts persistence logic. - The Unit of Work Pattern ensures multiple repository operations are committed as one transaction. - The Specification Pattern encapsulates query logic for reuse and composition. Together, they form a powerful trio for building clean, testable, and maintainable architectures in .NET Core.
| Repository pattern | NoSQL Integration (MongoDB, Redis) | |