Unit Of Work Pattern
Why we need unit of work pattern?
When we use repository design pattern we end up with creating repositories for every domain model. In real world application we need to manipulate (update/insert/delete) multiple model data together those are interdependent, then we end up with saving those model data separately, hence if one of the save operation get failed we do not have any way to revert the previous operations.
What is Unit Of work?
Unit of Work is referred to as a single transaction that involves multiple operations of insert/update/delete and so on kinds. To say it in simple words, it means that for a specific user action (say registration on a website), all the transactions like insert/update/delete and so on are done in one single transaction, rather then doing multiple database transactions. This means, one unit of work here involves insert/update/delete operations, all in one single transaction.
Unit of Work design pattern does two important things: first it maintains in-memory updates and second it sends these in-memory updates as one transaction to the database.
So to achieve through two steps:
Step 1: It maintains lists of business objects in-memory which have been changed (inserted, updated, or deleted) during a process.
Step 2: Once the process is completed, all these updates are sent as one big unit of work to be persisted physically in a database in one go.
Implementation
Step 1: Create a Interface called IUnitOfwork which contains in memory properties as follows –
public interface IUnitWork
{
ICustomer customers { get; set; }
IEmp emps { get; set; }
void Complete();
}
Step 2: Create a class UnitOfWork which implements IUnitOfWork and share a common context object to every repository.
public class UnitOfWork : IUnitWork
{
private CompanyContext cc;
public UnitOfWork()
{
cc = new CompanyContext();
}
private CustomerRepo custrepoobj;
private EmpRepo emprepoobj;
public ICustomer customers {
get
{
if (custrepoobj == null)
custrepoobj = new CustomerRepo(cc);
return custrepoobj;
}
set {
}
}
public IEmp emps {
get {
if (emprepoobj == null)
emprepoobj = new EmpRepo(cc);
return emprepoobj;
}
set { }
}
public void Complete()
{
cc.SaveChanges();
}
}
Step 3 : Modify every repository class including the generic repository so that same context object will be shared and remove SaveChanges method from every repository.
public class CustomerRepo :GenericRepo<Customer>,ICustomer
{
//contain actual data access code to work with customer entity
CompanyContext cc;
public CustomerRepo(CompanyContext temp):base(temp)
{
cc = temp;
}
}
Step 4: Modify the unity.config file to register the IUnitOfWork and UnitOfWork as follows –
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IUnitWork, UnitOfWork>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
Step 5: Modify the controller classes to use the unity.
public class CustController : Controller
{
// GET: Cust
IUnitWork crepo;
public CustController(IUnitWork ctemp)
{
this.crepo = ctemp;
}
public ActionResult Index()
{
return View(this.crepo.customers.GetAll());
}
[HttpGet]
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Customer rec)
{
if (ModelState.IsValid)
{
this.crepo.customers.Add(rec);
this.crepo.Complete();
return RedirectToAction("Index");
}
return View(rec);
}
}