Unit Of Work Pattern

Unit of Work in C# Repository Pattern

The Unit of Work Pattern in C# is used to group one or more operations (usually database CRUD operations) into a single transaction and execute them by applying the principle of do everything or do nothing. That means if any of the operations in the transaction failed, then it will roll back the transaction. If all the operations are successful, then it will commit the transaction. So, it will execute all the database operations as one unit.

Problems

In the Repository Pattern, it is easy to realize that we have more than one repository, as in the example in part 1 we have 3 repositories (CustomerRepository, OrderRepository and ProductRepository). If these 3 guys work separately, ie editing data each guy does not affect the other guy, we don't talk about it, but in this case Order and Customer are related. When we add 1 Order, we will first check if the Customer list is available or not? if yes then we will get that Customer from CustomerRepository otherwise we will have to create a new instance.

public IActionResult Create(CreateOrderModel model)
{
    if (!model.LineItems.Any()) return BadRequest("Please submit line items");

    if (string.IsNullOrWhiteSpace(model.Customer.Name)) return BadRequest("Customer needs a name");

    var customer = CustomerRepository.Find(c => c.Name == model.Customer.Name).FirstOrDefault();

    if (customer != null)
    {
        customer.ShippingAddress = model.Customer.ShippingAddress;
        customer.PostalCode = model.Customer.PostalCode;
        customer.City = model.Customer.City;
        customer.Country = model.Customer.Country;

        customerRepository.Update(customer);
        customerRepository.SaveChanges();
    }
    else
    {
        customer = new Customer
        {
            Name = model.Customer.Name,
            ShippingAddress = model.Customer.ShippingAddress,
            City = model.Customer.City,
            PostalCode = model.Customer.PostalCode,
            Country = model.Customer.Country
        };
    }
    var order = new Order
    {
        LineItems = model.LineItems
            .Select(line => new LineItem { ProductId = line.ProductId, Quantity = line.Quantity })
            .ToList(),

        Customer = customer
    };

    orderRepository.Add(order);

    orderRepository.SaveChanges();

    return Ok("Order Created");
}

As the above example in the same function, we have to call the database up to 4 times, actually both customerRepository and orderRepository are dbContext in EntityFramework but they are 2 different instances leading to having to call the database many times for the update. of each guy. So to improve efficiency, why not create a class containing all these guys, when you change something, only update it once.

Design

First we need to create a UnitOfWork class that will hold all the repositories we have. This class I wrote in the Infrastructure layer together with the repositories

public interface IUnitOfWork
{
    IRepository<Customer> CustomerRepository { get; }
    IRepository<Order> OrderRepository { get; }
    IRepository<Product> ProductRepository { get; }

    void SaveChanges();
}

public class UnitOfWork : IUnitOfWork
{
    private ShoppingContext context;

    public UnitOfWork(ShoppingContext context)
    {
        this.context = context;
    }

    private IRepository<Customer> customerRepository;
    public IRepository<Customer> CustomerRepository
    {
        get
        {
            if (customerRepository == null)
            {
                customerRepository = new CustomerRepository(context);
            }

            return customerRepository;
        }
    }

    private IRepository<Order> orderRepository;
    public IRepository<Order> OrderRepository
    {
        get
        {
            if(orderRepository == null)
            {
                orderRepository = new OrderRepository(context);
            }

            return orderRepository;
        }
    }

    private IRepository<Product> productRepository;
    public IRepository<Product> ProductRepository
    {
        get
        {
            if (productRepository == null)
            {
                productRepository = new ProductRepository(context);
            }

            return productRepository;
        }
    }

    public void SaveChanges()
    {
        context.SaveChanges();
    }
}

The IUnitOfWork class is an interface that includes the repositories, this way instead of injecting each repository where it needs to be used, just inject this UnitOfWork class and every time it needs to be used, it will initialize the instance for that repository. As mentioned, only when called, we will create an instance for it and have an extra SaveChange function, when calling this function, it will simultaneously update all Repository, this function is provided by Enity Framework, you guys don't have to wonder. Why can't you do it?

After creating the UnitOfWork class, we inject it where we need to use it, for example in OrderController or because we have to inject multiple repository instances, just inject this class to be able to use all the repositories.

//private readonly IRepository<Order> orderRepository;
//private readonly IRepository<Product> productRepository;

//public OrderController(IRepository<Order> orderRepository,
//     IRepository<Product> productRepository)
//{
//    this.orderRepository = orderRepository;
//    this.productRepository = productRepository;
//}

private readonly IUnitOfWork unitOfWork;

public OrderController(IUnitOfWork unitOfWork)
{
    this.unitOfWork = unitOfWork;
}

And using it is also very simple, just add the prefix unitOfWork before the original functions and use it without any further editing.

public IActionResult Index()
{
    var orders = unitOfWork.OrderRepository.Find(order => order.OrderDate > DateTime.UtcNow.AddDays(-1));

    return View(orders);
}

public IActionResult Create()
{
    var products = unitOfWork.ProductRepository.All();

    return View(products);
}

[HttpPost]
public IActionResult Create(CreateOrderModel model)
{
    if (!model.LineItems.Any()) return BadRequest("Please submit line items");

    if (string.IsNullOrWhiteSpace(model.Customer.Name)) return BadRequest("Customer needs a name");

    var customer = unitOfWork.CustomerRepository
        .Find(c => c.Name == model.Customer.Name)
        .FirstOrDefault();

    if(customer != null)
    {
        customer.ShippingAddress = model.Customer.ShippingAddress;
        customer.PostalCode = model.Customer.PostalCode;
        customer.City = model.Customer.City;
        customer.Country = model.Customer.Country;

        unitOfWork.CustomerRepository.Update(customer);
    }
    else
    {
        customer = new Customer
        {
            Name = model.Customer.Name,
            ShippingAddress = model.Customer.ShippingAddress,
            City = model.Customer.City,
            PostalCode = model.Customer.PostalCode,
            Country = model.Customer.Country
        };
    }

    var order = new Order
    {
        LineItems = model.LineItems
            .Select(line => new LineItem { ProductId = line.ProductId, Quantity = line.Quantity })
            .ToList(),

        Customer = customer
    };

    unitOfWork.OrderRepository.Add(order);

    unitOfWork.SaveChanges();

    return Ok("Order Created");
}

Comparing the Create function in OrderController now with the original, we see that the number of calls to the database is significantly reduced, this is the benefit of applying the Unit Of Work Pattern in the project.

Last updated