Everything You Need To Know About Unit Of Work Design Pattern
Unit Of Work pattern is a pattern that we use to manage database operations. That's why we generally create it on data tier in n-tier Architecture. We take our all repository classes in one base class and use it from there. So still we will be creating our repository classes but instead of calling them directly we will be calling them from one base class and save them in this class. And that’s how it will be easier to manage them.
Let’s see it on an example:
The scenario is that we will let companies to create account and they will be able to add personals for their corporation. Let’s code it.
First step will be creating database entity classes.
These are company and user classes:
public class Company
{
public int Id { get; set; }
public string CompanyName { get; set; }
[DataType(DataType.EmailAddress)]
public string EmailAddress { get; set; }
public string Password { get; set; }
public string Detail { get; set; }
[NotMapped]
public List<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[ForeignKey("Company")]
[NotMapped]
public int CompanyId { get; set; }
[NotMapped]
public Company Company { get; set; }
}
Creating Repository Classes
We will have two repository classes one for User and one for the Company and we will add some data operations like Add and Delete.
That’s the first repository that we have for the user database operations:
public interface IUserRepository
{
void Add(User user);
void Delete(User user);
}
public class UserRepository : IUserRepository
{
private readonly ApplicationDbContext _context;
public UserRepository(ApplicationDbContext context)
{
_context = context;
}
public void Add(User user)
{
_context.Users.Add(user);
_context.Save();
}
public void Delete(User user)
{
_context.Users.Add(user);
_context.Save();
}
public void Save() // we will not need to use save method with Unit Of Work
{
_context.Save();
}
}
Now let’s create for company:
public interface ICompanyRepository
{
void Add(Company company);
void Delete(Company company);
}
public class CompanyRepository : ICompanyRepository
{
private readonly ApplicationDbContext _context;
public CompanyRepository(ApplicationDbContext context)
{
_context = context;
}
public void Add(Company company)
{
_context.Companies.Add(company);
}
public void Delete(Company company)
{
_context.Companies.Remove(company);
}
public void Save() // we will not need to use save method with Unit Of Work
{
_context.Save();
}
}
We have created database operations for User and Company. Now let’s use the normal using then implement Unit of work to understand what difference it makes when we make database operations.
[Route("api/[controller]")]
[ApiController]
public class DemoController : ControllerBase
{
private readonly ICompanyRepository _companyRepository;
private readonly IUserRepository _userRepository;
public DemoController(ICompanyRepository companyRepository,
IUserRepository userRepository)
{
_companyRepository = companyRepository;
_userRepository = userRepository;
}
[HttpPost]
public IActionResult CreateUserAndCompany()
{
User user = new User
{
FirstName = "Test",
LastName = "Test",
};
Company company = new Company
{
CompanyName = "First Company",
Detail = "First Company",
EmailAddress = "first@gmail.com",
Password = "",
};
_userRepository.Add(user);
_companyRepository.Add(company);
_unitOfWork.Save();
return Ok();
}
}
Normally we can call and use these two repository with that way but instead of calling these two operations separately we will use the advantage of Unit Of Work pattern. Now its time to use it and then we will change this controller part.
Implementing Unit Of Work.
public interface IUnitOfWork
{
IUserRepository user { get; }
ICompanyRepository company { get;}
void Save();
}
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _context;
public IUserRepository user { get; private set; }
public ICompanyRepository company { get; private set ; }
public UnitOfWork(ApplicationDbContext context)
{
_context = context;
user = new UserRepository(context);
company = new CompanyRepository(context);
}
public void Save()
{
_context.SaveChanges();
}
}
As you can see from the code that now we added our repositories to the Unit Of Work as properties and created its constructor. So when we create a unit of work class we will be able to access repositories and call their methods there the only thing that we should not forget that: after the operations we should call save method.
[Route("api/[controller]")]
[ApiController]
public class DemoController : ControllerBase
{
private readonly IUnitOfWork _unitOfWork;
public DemoController(IUnitOfWork unitOfWırk)
{
_unitOfWork = unitOfWırk;
}
[HttpPost]
public IActionResult CreateUserAndCompany()
{
User user = new User
{
FirstName = "Test",
LastName = "Test",
};
Company company = new Company
{
CompanyName = "First Company",
Detail = "First Company",
EmailAddress = "first@gmail.com",
Password = "",
};
_unitOfWork.company.Add(company);
_unitOfWork.user.Add(user);
_unitOfWork.Save();
return Ok();
}
}
As you could understand from the code snippet now we are making database operations by using Unit Of Work implementation because we can manage all repositories from there. And we are deciding to save operations with it. So when we are making multiple operations we can handle exceptions better and can decide where to save the changes that we have made.
That’s how we can make the implementation for the Unit Of Work pattern. Unit of work pattern generally using with Generic Repository and I have an article about Generic Repository you can access it from here. It will make more sense to use both pattern together in a project.
You can access the example Source Code from here
Thank you for reading I hope it gave you a clear understanding about what it is and how to implement it to your project.
If you liked the article Don’t forget to claps and follow on medium. Happy Coding.