DEV Community

Saumya-Ranjan-Mishra
Saumya-Ranjan-Mishra

Posted on

Unit Of Work In ASP.Net Core

Unit Of Work.. What is it ?

The Unit of Work pattern is a design pattern used to group multiple operations (usually repository calls) into a single transaction. It ensures that either all operations succeed or none are applied, maintaining data consistency.

It usually works alongside the Repository pattern, which abstracts data access logic.

As I said it can work with repository patten, I will keep the code fairly simple rather than over-complicating it with generic repository.

Setting up the repositories first...

entities
// Models/Product.cs public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } // Models/Order.cs public class Order { public int Id { get; set; } public int ProductId { get; set; } public DateTime OrderDate { get; set; } } // Models/Log.cs public class Log { public int Id { get; set; } public string Message { get; set; } public DateTime Timestamp { get; set; } } 
Enter fullscreen mode Exit fullscreen mode
set the DbContext
public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {} public DbSet<Product> Products { get; set; } public DbSet<Order> Orders { get; set; } public DbSet<Log> Logs { get; set; } } 
Enter fullscreen mode Exit fullscreen mode
Define the repositories
public interface IProductRepository { Task<Product> GetByIdAsync(int id); Task AddAsync(Product product); void Update(Product product); void Delete(Product product); } public interface IOrderRepository { Task AddAsync(Order order); } public interface ILogRepository { Task AddAsync(Log log); } 
Enter fullscreen mode Exit fullscreen mode
Implement the repositories
// Product repositories public class ProductRepository : IProductRepository { private readonly AppDbContext _context; public ProductRepository(AppDbContext context) { _context = context; } public async Task<Product> GetByIdAsync(int id) { return await _context.Products.FindAsync(id); } public async Task AddAsync(Product product) { await _context.Products.AddAsync(product); } public void Update(Product product) { _context.Products.Update(product); } public void Delete(Product product) { _context.Products.Remove(product); } } 
Enter fullscreen mode Exit fullscreen mode
public class OrderRepository : IOrderRepository { private readonly AppDbContext _context; public OrderRepository(AppDbContext context) { _context = context; } public async Task AddAsync(Order order) { await _context.Orders.AddAsync(order); } } 
Enter fullscreen mode Exit fullscreen mode
public class LogRepository : ILogRepository { private readonly AppDbContext _context; public LogRepository(AppDbContext context) { _context = context; } public async Task AddAsync(Log log) { await _context.Logs.AddAsync(log); } } 
Enter fullscreen mode Exit fullscreen mode

Unit of Work Interface & Implementation

public interface IUnitOfWork : IDisposable { IProductRepository Products { get; } IOrderRepository Orders { get; } ILogRepository Logs { get; } Task<int> CompleteAsync(); } 
Enter fullscreen mode Exit fullscreen mode
Implementation of IUnitOfWork
public class UnitOfWork : IUnitOfWork { private readonly AppDbContext _context; public IProductRepository Products { get; } public IOrderRepository Orders { get; } public ILogRepository Logs { get; } public UnitOfWork( AppDbContext context, IProductRepository productRepository, IOrderRepository orderRepository, ILogRepository logRepository) { _context = context; Products = productRepository; Orders = orderRepository; Logs = logRepository; } public async Task<int> CompleteAsync() { return await _context.SaveChangesAsync(); } public void Dispose() { _context.Dispose(); } } 
Enter fullscreen mode Exit fullscreen mode

Inject the dependencies

Make sure you register the dependencies as AddScoped, so that all the repositories will share the same instance of DbContext in a particular http call.

services.AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddScoped<IProductRepository, ProductRepository>(); services.AddScoped<IOrderRepository, OrderRepository>(); services.AddScoped<ILogRepository, LogRepository>(); services.AddScoped<IUnitOfWork, UnitOfWork>(); 
Enter fullscreen mode Exit fullscreen mode

Operations in controller

[ApiController] [Route("api/[controller]")] public class CheckoutController : ControllerBase { private readonly IUnitOfWork _unitOfWork; public CheckoutController(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; } [HttpPost] public async Task<IActionResult> Checkout([FromBody] ProductOrderDto dto) { var product = new Product { Name = dto.ProductName, Price = dto.Price }; var order = new Order { ProductId = product.Id, OrderDate = DateTime.UtcNow }; var log = new Log { Message = "New order placed", Timestamp = DateTime.UtcNow }; await _unitOfWork.Products.AddAsync(product); await _unitOfWork.Orders.AddAsync(order); await _unitOfWork.Logs.AddAsync(log); // Single transaction await _unitOfWork.CompleteAsync(); return Ok("Order placed successfully."); } } 
Enter fullscreen mode Exit fullscreen mode

Congratulation! You have successfully implemented unit of work there by saving your DB from a possible inconsistent state.

Feel free to ask any question. Or ping in linkedin

Top comments (0)