Learn ASP.NET MVC, ASP.NET Core, and Design Patterns through our online training programs. Courses conducted by Bipin Joshi on weekends. Read more details here.

Using Unit of Work Pattern in AS

Using Unit of Work Pattern in ASP.NET MVC and Entity Framework

Recently I wrote an article explaining the Repository Pattern and its use in ASP.NET MVC. This article builds on that article and explains another related design pattern - Unit of Work.

The Unit of Work pattern states its intent like this:

Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.

Let's try to understand the above statement with an example. Let's say you are building an order processing system that accepts orders through a shopping cart. The whole order information is divided in two parts - OrderHeader and OrderDetails. The OrderHeader includes details that are applicable to the order as a whole including Customer ID, Order Date and Shipping Address. The OrderDetails indicate the individual items that make the order. They include details such as Product ID, Quantity and Unit Price. For one OrderHeader record there will be one or more OrderDetails records.

Now, further suppose that you have created repositories for these two pieces in your application as described in this article. Let's call them OrderHeaderRepository and OrderDetailsRepository respectively. If you instantiate and use these two repositories independently you will be above to place a new order but there will be a problem. Since using these repositories indecently means using altogether different instances of the Entity Framework data context (NorthwindEntities in the above example), you won't be able to cancel an order if there is some error while adding an order item. This means adding OrderHeader and adding OrderDetails won't work as a single business transaction. Obviously, this can produce incorrect results because both of these steps must be executed as a single unit of work.

The problem here is that both of the repositories are used independently. Instead, if you make both of them together such that they use the same data context you can enforce the above mentioned business rule easily. That is where Unit of Work pattern comes into picture. A class implementing Unit of Work pattern maintains a list of all the objects that are participating a business transaction. It then uses them together so as to execute all the steps as a part of single business transaction. So, in our example you need to create a class that houses both OrderHeaderRepository and OrderDetailsRepository. This class then supplies them the same EF data context so that all the database queries are executed through the same data context. Since a data context is being shared by all the participating objects you get chance to control how the individual steps of an operation are executed or canceled.

EF data context class itself is an example of Unit of Work pattern. It maintains one or more DbSet objects and calling SaveChanges() attempts to take changes from all the DbSet objects to the database.

Ok. Now let's write some code that shows how Unit of Work pattern can be used along with the Repository pattern. For the sake of simplicity I am not going to create generic repositories for OrderHeader and OrderDetails objects. You may read the above mentioned article for more information.

Let's add two interfaces that govern how the two repositories look like:

public interface IOrderHeaderRepository
{
    IEnumerable<Order> SelectAll();
    Order SelectByID(string id);
    void Insert(Order obj);
    void Update(Order obj);
    void Delete(string id);
    void Save();
}

public interface IOrderDetailsRepository
{
    IEnumerable<Order_Detail> SelectAll();
    Order_Detail SelectByID(string id);
    void Insert(Order_Detail obj);
    void Update(Order_Detail obj);
    void Delete(string id);
    void Save();
}

The IOrderHeaderRepository interface is implemented by the OrderHeaderRepository class and defines methods to perform CRUD operations on the Orders table. The OrderHeaderRepository class is shown below:

public class OrderHeaderRepository:IOrderHeaderRepository
{
    private NorthwindEntities db = null;

    public OrderHeaderRepository()
    {
        this.db = new NorthwindEntities();
    }

    public OrderHeaderRepository(NorthwindEntities db)
    {
        this.db = db;
    }

    public IEnumerable<Order> SelectAll()
    {
        return db.Orders.ToList();
    }

    public Order SelectByID(string id)
    {
        return db.Orders.Find(id);
    }

    public void Insert(Order obj)
    {
        db.Orders.Add(obj);
    }

    public void Update(Order obj)
    {
        db.Entry(obj).State = EntityState.Modified;
    }

    public void Delete(string id)
    {
        Order existing = db.Orders.Find(id);
        db.Orders.Remove(existing);
    }

    public void Save()
    {
        db.SaveChanges();
    }
}

We won't go into the details of these methods here as they are quite straightforward. The OrderDetailsRepository performs CRUD on [Order Details] table and is shown below:

public class OrderDetailsRepository : IOrderDetailsRepository
{
    private NorthwindEntities db = null;

    public OrderDetailsRepository()
    {
        this.db = new NorthwindEntities();
    }

    public OrderDetailsRepository(NorthwindEntities db)
    {
        this.db = db;
    }

    public IEnumerable<Order_Detail> SelectAll()
    {
        return db.Order_Details.ToList();
    }

    public Order_Detail SelectByID(string id)
    {
        return db.Order_Details.Find(id);
    }

    public void Insert(Order_Detail obj)
    {
        db.Order_Details.Add(obj);
    }

    public void Update(Order_Detail obj)
    {
        db.Entry(obj).State = EntityState.Modified;
    }

    public void Delete(string id)
    {
        Order_Detail existing = db.Order_Details.Find(id);
        db.Order_Details.Remove(existing);
    }

    public void Save()
    {
        db.SaveChanges();
    }
}

Now comes the main point of our discussion. Add a class and name it OrderUnitOfWork. Then key-in the following code in the OrderUnitOfWork class.

public class OrderUnitOfWork
{
    private NorthwindEntities db = null;
    private OrderHeaderRepository ordRepository = null;
    private OrderDetailsRepository orddetailsRepository = null;

    public OrderUnitOfWork()
    {
        db = new NorthwindEntities();
        ordRepository = new OrderHeaderRepository(db);
        orddetailsRepository = new OrderDetailsRepository(db);
    }

    public OrderRepository OrderHeaders
    {
        get
        {
            return ordRepository;
        }
    }

    public OrderDetailsRepository OrderDetails
    {
        get
        {
            return orddetailsRepository;
        }
    }

    public void PlaceOrder()
    {
        //other checking or logic here
        db.SaveChanges();
    }
}

The OrderUnitOfWork class declares three private variables of type NorthwindEntities, OrderHeaderRepository and OrderDetailsRepository respectively. In the constructor of the OrderUnitOfWork class a new data context is instantiated and the same is passed to the constructors of OrderHeaderRepository and OrderDetailsRepository respectively.

The class also defines two public properties OrderHeaders and OrderDetails that return the instances of OrderHeaderRepository and OrderDetailsRepository respectively. These instances will be used by your controller code to add the header and details information of an order. The PlaceOrder() method calls SaveChanges() method of the data context to save all the changes made to headers and details to the database.

Once created you can use OrderUnitOfWork class in your controller like this:

public ActionResult PlaceOrder()
{
    OrderUnitOfWork workUnit = new OrderUnitOfWork();

    OrderHeaderRepository ord = workUnit.OrderHeaders;
    OrderDetailsRepository orddetails = workUnit.OrderDetails;

    Order newOrder = new Order();
    newOrder.OrderID = 100;
    newOrder.CustomerID = "ALFKI";
    newOrder.EmployeeID = 1;
    newOrder.OrderDate = DateTime.Now;
    ord.Insert(newOrder);

    Order_Detail newOrderDetail = new Order_Detail();
    newOrderDetail.OrderID = newOrder.OrderID;
    newOrderDetail.ProductID = 200;
    newOrderDetail.Quantity = 5;
    newOrderDetail.UnitPrice = 1234.56M;
    orddetails.Insert(newOrderDetail);

    workUnit.PlaceOrder();

    return View();
}

The PlaceOrder() action method creates in instance of OrderUnitOfWork class. A reference to OrderHeaders and OrderDetails repositories are stored in two local variables for easy access. Then a new Order is added using OrderHeaderRepository and a new OrderD_Detail is added using OrderDetailsRepository respectively. Finally, PlaceOrder() method of the OrderUnitOfWork is called to save the order details.




Bipin Joshi is a software consultant, trainer, author and a yogi having 21+ years of experience in software development. He conducts online courses in ASP.NET MVC / Core, jQuery, AngularJS, and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced Yoga way of life he also teaches Ajapa Meditation to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 15 Apr 2014



Tags : ASP.NET ADO.NET Data Access MVC