s
The Unit of Work (UoW) pattern is another design pattern commonly used in conjunction with the Repository pattern, particularly in data-driven applications. It helps manage transactions and ensures that multiple operations across multiple repositories can be committed or rolled back as a single unit. This is particularly useful in scenarios where a series of changes need to be atomic, maintaining data integrity.
Here’s a step-by-step guide to implementing the Unit of Work pattern alongside the Repository pattern in an ASP.NET Core application.
Start by defining an interface for the Unit of Work, which will provide methods to manage repositories and commit changes.
public interface IUnitOfWork : IDisposable
{
IProductRepository Products { get; }
ICategoryRepository Categories { get; }
Task<int> CompleteAsync(); // Saves all changes made in this unit of work
}
Next, create a class that implements the IUnitOfWork
interface.
This class will manage the repositories and the database context.
public class UnitOfWork : IUnitOfWork
{
private readonly YourDbContext _context;
public UnitOfWork(YourDbContext context)
{
_context = context;
Products = new ProductRepository(_context);
Categories = new CategoryRepository(_context);
}
public IProductRepository Products { get; private set; }
public ICategoryRepository Categories { get; private set; }
public async Task<int> CompleteAsync()
{
return await _context.SaveChangesAsync();
}
public void Dispose()
{
_context.Dispose();
}
}
Make sure your repositories accept a DbContext instance through their constructors so they can share the same context instance managed by the Unit of Work.
public class ProductRepository : IProductRepository
{
private readonly YourDbContext _context;
public ProductRepository(YourDbContext context)
{
_context = context;
}
// Implement repository methods...
}
In your Program.cs for .NET 8), register the UnitOfWork with the dependency injection container.
services.AddDbContext<YourDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddControllers();
Finally, use the Unit of Work in your controllers to manage transactions across multiple repositories.
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IUnitOfWork _unitOfWork;
public ProductsController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
[HttpPost]
public async Task<IActionResult> AddProduct(Product product)
{
await _unitOfWork.Products.AddAsync(product);
await _unitOfWork.CompleteAsync(); // Commit changes
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateProduct(int id, Product product)
{
if (id != product.Id)
{
return BadRequest();
}
await _unitOfWork.Products.UpdateAsync(product);
await _unitOfWork.CompleteAsync(); // Commit changes
return NoContent();
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteProduct(int id)
{
await _unitOfWork.Products.DeleteAsync(id);
await _unitOfWork.CompleteAsync(); // Commit changes
return NoContent();
}
}
The Unit of Work pattern, when combined with the Repository pattern, provides a powerful way to manage data access in ASP.NET Core applications. It helps maintain transactional integrity, centralizes data access logic, and improves code maintainability and testability.