Entity Framework and the Unit of Work pattern<!-- --> | <!-- -->Patrick Desjardins Blog
Patrick Desjardins Blog
Patrick Desjardins picture from a conference

Entity Framework and the Unit of Work pattern

Posted on: December 16, 2013


This article is a summary of how to make the use of a unit of work with Entity Framework. First of all, Entity Framework is a unit of work by itself. You can do multiple insert, update and delete and it's not until a SaveChanges that everything is committed to the Sql Server. The problem is that you may want to have multiple repositories. This mean that if you want to be under the same transaction that you want to share the save DbContext. Here comes the unit of work, a pattern that share the DbContext. The reference of DbContext is shared across repositories, which is interesting because if we want to be domain driven we can share the DbContext between repositories of the same domain. It's also interesting for unit testing. The reason is that the unit of work has interface which can be easily mocked.

I have seen an article on Asp.Net website concerning Entity Framework and the unit of work pattern but I believe it's wrong. I prefer the one of Julie Lerman in her Pluralsight video. The main reason is the one of Asp.Net includes the repository inside the unit of work and the DbContext. The one of Julie Lerman only contain the DbContext and the unit of work is passed through every repositories of the domain.

Here is the representation of every layers that we would like with the unit of work.


As you can see, the controller should contact the service layer where all queries are from databases, access to caching services and web services are executed. For the database part, we contact the data access layer accessor which is an abstraction for the unit of work and repositories. This allow every developers that use repositories to abstract the need to create the unit of work and to pass it through constructors. The accessor does have a reference to repositories and to the unit of work.

This article explains how to create a layered approach that has a controller, a service layer, a data access layer accessor with repositories and unit of work with a simple set of entities. I already have wrote an article for repository and entity framework. This was an other simpler way to design the repository. Previously, a facade was passing the DbContext to all repository, which was created the same behavior as the unit of work pattern. However, the unit of work is more elaborate and allows to unit test easily and allow you to reuse repository in several DbContext if required. Having the possibility to create several DbContext and to share it by domain (for domain driven design) is important for big software. It increase the performance of the database context by having a limited amount of entity to handle. So, the previous way to handle repository is perfect if you have under 50 entities. This is a rule of thumb and it depends of many factors. If you have a lot of entities and that you can draw specific domains, the approach of unit of work in this post is preferable. As you will see, a lot of more classes will be needed and this is not a small detail to consider before going into this way.

Creating the entities, the database context and the tables

First of all, let's create entities and a simple context that we will call directly from the controller. This should never been done in enterprise but it will allow us to migrate the code from a simple basic code to a more heavy layered application.

1public class Animal {
2 public int Id { get; set; }
3 public string Name { get; set; }
5 public virtual ICollection<Animal> Enemies { get; set; }
6 public virtual ICollection<Animal> EnemyOf { get; set; }
9public class Cat : Animal {
10 public int NumberOfMustache { get; set; }
11 public int RemainingLife{get;set;}
14public class Dog : Animal {
15 public string Type { get; set; }

We have two classes, one is for Cat and one is for Dog. Both inherit from Animal class. These are very simple classes because we want to focus on the unit of work and not on complex classes. The next step is to create the database context.

The first step is to get Entity Framework. This can be done by using Nuget with the interface ("Manage Nuget Package") or with a command line :

1PM> install-package entityframework

Then, we need to inherit from DbContext and setup web.config to have a connection string for the database. The web.config looks like this:

2 <configSections>
3 <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
4 </configSections>
5 <connectionStrings>
6 <add name="EntityConnectionString" connectionString="Data Source=PATRICK-I7\\SQLEXPRESS;Initial Catalog=UnitOfWork;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
7 </connectionStrings>

The first is that we have a new configSection for Entity Framework. This has been added automatically. The line that is required to be added manually is the connection string.

The last step is to configure the entity. Since we are simplify the whole application for the purpose of the unit of work, the model class will be directly the entity. Some may want in enterprise application have an additional layer to not share entity classes with the model.

1public class AllDomainContext:DbContext {
2 public AllDomainContext():base("EntityConnectionString") { }
4 protected override void OnModelCreating(DbModelBuilder modelBuilder) {
5 base.OnModelCreating(modelBuilder);
7 //Table per type configuration
8 modelBuilder.Entity<Dog>().ToTable("Animals");
9 modelBuilder.Entity<Dog>().ToTable("Dogs");
10 modelBuilder.Entity<Cat>().ToTable("Cats");
12 //Primary keys configuration
13 modelBuilder.Entity<Animal>().HasKey(k => k.Id);
15 modelBuilder.Entity<Animal>()
16 .HasMany(entity => entity.Enemies)
17 .WithMany(d => d.EnemyOf)
18 .Map(d => d.ToTable("Animals_Enemies_Association").MapLeftKey("AnimalId").MapRightKey("EnemyId"));
19 }

The configuration has something special for the Enemies list because I did not wanted to handle the the association table by myself. Entity Framework can handle it for us by configure a many to many relationship with the animal class. It requires to have a table name for the many-many table with a foreign keys.

Setup the controllers, service layer and data access layer

Before even having the service layer, let's use the context directly into the controller and see the database creation. Then, we will change to code every layers but not the unit of work yet. We can use scaffolding to leverage Visual Studio power to get code generation for us. First step, right click the controller and select add new controller.

MvcControllerWithEntityFramework 400x278

The second step is to select to model class, you can select the one of Animal and select the DbContext class. If you do not see your DbContext class (DatabaseContext), close the window and compile your application. The wizard bases its choice on the compiled resource of the project. Once generated, you can execute the code, IIS Express start by default and you just need to go to http://localhost:15635/Animal and the DbContext will start the creation of the database. If you open SQL Server Manager, the unit of work database should have 3 tables.


Transforming to have service layers

At this stage, the architecture of the web application is not enterprise grade. The controller has a strong reference to the database context. The next step is to have everything related to the database inside a service layer which abstract entity framework. This allow us to test easily the controller without having to care about the database.

This is the current controller code at this moment.

1public class AnimalController : Controller {
2 private DatabaseContext db = new DatabaseContext();
4 public ActionResult Index() { return View(db.Animals.ToList()); }
6 public ActionResult Details(int? id) {
7 if (id == null) {
8 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
9 }
10 Animal animal = db.Animals.Find(id);
11 if (animal == null) {
12 return HttpNotFound();
13 }
14 return View(animal);
15 }
17 public ActionResult Create() { return View(); }
19 [HttpPost]
20 [ValidateAntiForgeryToken]
21 public ActionResult Create([Bind(Include="Id,Name")] Animal animal) {
22 if (ModelState.IsValid) {
23 db.Animals.Add(animal); db.SaveChanges(); return RedirectToAction("Index");
24 }
26 return View(animal);
27 }
29 public ActionResult Edit(int? id) {
30 if (id == null) {
31 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
32 }
33 Animal animal = db.Animals.Find(id);
34 if (animal == null) {
35 return HttpNotFound();
36 }
37 return View(animal);
38 }
40 [HttpPost]
41 [ValidateAntiForgeryToken]
42 public ActionResult Edit([Bind(Include="Id,Name")] Animal animal) {
43 if (ModelState.IsValid) {
44 db.Entry(animal).State = EntityState.Modified;
45 db.SaveChanges();
46 return RedirectToAction("Index");
47 }
48 return View(animal);
49 }
51 public ActionResult Delete(int? id) {
52 if (id == null) {
53 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
54 }
55 Animal animal = db.Animals.Find(id);
56 if (animal == null) {
57 return HttpNotFound();
58 }
59 return View(animal);
60 }
62 [HttpPost, ActionName("Delete")]
63 [ValidateAntiForgeryToken]
64 public ActionResult DeleteConfirmed(int id) {
65 Animal animal = db.Animals.Find(id); db.Animals.Remove(animal);
66 db.SaveChanges();
67 return RedirectToAction("Index");
68 }
70 protected override void Dispose(bool disposing) {
71 if (disposing) {
72 db.Dispose();
73 }
74 base.Dispose(disposing);
75 }

If you want to test rapidly the database, just add the code in the index.

1public ActionResult Index() {
2 var animal1 = new Animal { Name = "Boss" };
3 var cat1 = new Cat { Name = "Mi" };
4 var cat2 = new Cat { Name = "Do" };
5 animal1.Enemies = new List<Animal> { cat1,cat2};
6 db.Animals.Add(animal1);
7 db.Animals.Add(cat1);
8 db.Animals.Add(cat2);
9 db.SaveChanges();
10 return View(db.Animals.AsNoTracking().ToList());

tablesDataForAssociation 400x309

The first step is to create a repository class for animal inside the DataAccessLayer folder. Normally, I create a folder called Repository to have all repositories.

1public class AnimalRepository : IAnimalRepository {
2 private DatabaseContext db = new DatabaseContext();
4 public Models.Animal Find(int? id) { return db.Animals.Find(id); }
6 public void Insert(Models.Animal animal) { db.Animals.Add(animal); db.SaveChanges(); }
8 public void Update(Models.Animal animal) { db.Entry(animal).State = EntityState.Modified; db.SaveChanges(); }
10 public void Delete(Models.Animal animal) { db.Animals.Remove(animal); db.SaveChanges(); }
12 public void Dispose() { db.Dispose(); }
14 public IList<Animal> GetAll() { return db.Animals.AsNoTracking().ToList(); }

This class as also an interface with the public method in it.

The second step is to create a service layer. Normally, we would create a new project, but to keep everything simple, let's just add a new folder (namespace). Then, we move the DatabaseContext class from the controller to the service.

The animal service class looks like the following code.

1public class AnimalService: IAnimalService {
2 private IAnimalRepository animalRepository;
4 public AnimalService(IAnimalRepository animalRepository) { this.animalRepository = animalRepository; }
6 public Models.Animal Find(int? id) { return this.animalRepository.Find(id); }
8 public void Insert(Models.Animal animal) { this.animalRepository.Insert(animal); }
10 public void Update(Models.Animal animal) { this.animalRepository.Update(animal); }
12 public void Delete(Models.Animal animal) { this.animalRepository.Delete(animal); }
14 public IList<Animal> GetAll() { return this.animalRepository.GetAll(); }

It's all the code from the controller. Later, some improvement should be done. One of this change is to move the SaveChanges because it's not interesting to save every time we add, modify or update an entity. This cause performance problem when several entities are required to be posted to the database. However, let's focus on the transformation first, later these details will be gone. The role of the service layer is to resemble every repository. In this situation we have only one repository. In fact, in more complex problem like in enterprise, a service has several repository and caching classes.

The next class that require changes is the animal controller class. This one now has a constructor that need an IAnimalService.

1public class AnimalController : Controller {
2 private IAnimalService_service;
4 public AnimalController() {_service = new AnimalService(new AnimalRepository()); }
6 public AnimalController(IAnimalService animalService) {_service = animalService; }
8 public ActionResult Index() { return View(_service.GetAll()); }
10 public ActionResult Details(int? id) {
11 if (id == null) {
12 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
13 }
14 Animal animal =_service.Find(id);
15 if (animal == null) {
16 return HttpNotFound();
17 }
18 return View(animal);
19 }
21 public ActionResult Create() { return View(); }
23 [HttpPost]
24 [ValidateAntiForgeryToken]
25 public ActionResult Create([Bind(Include="Id,Name")] Animal animal) {
26 if (ModelState.IsValid) {
27 _service.Insert(animal);
28 return RedirectToAction("Index");
29 }
31 return View(animal); }
33 public ActionResult Edit(int? id) { if (id == null) {
34 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
35 }
37 Animal animal =_service.Find(id);
39 if (animal == null) { return HttpNotFound(); }
41 return View(animal); }
43 [HttpPost]
44 [ValidateAntiForgeryToken]
45 public ActionResult Edit([Bind(Include="Id,Name")] Animal animal) {
46 if (ModelState.IsValid) {
47 _service.Update(animal);
48 return RedirectToAction("Index");
49 }
50 return View(animal);
51 }
53 public ActionResult Delete(int? id) {
54 if (id == null) {
55 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
56 }
57 Animal animal =_service.Find(id);
58 if (animal == null) {
59 return HttpNotFound();
60 }
62 return View(animal);
63 }
65 [HttpPost, ActionName("Delete")]
66 [ValidateAntiForgeryToken]
67 public ActionResult DeleteConfirmed(int id) {
68 Animal animal =_service.Find(id);
69 _service.Delete(animal);
70 return RedirectToAction("Index");
71 }

At this stage, the controller is separated from the database by the service and the repository. Still, it's better to not having a strong reference to AnimalService inside the controller. This is why we will extract an interface from AnimalService and we will inject the concrete class by inversion of control. This allow us to have when doing test entry point to inject a fake AnimalService that won't goes to the database. You can use the refactoring tool to extract the interface easily.

ExtractInterface 400x281

1public interface IAnimalService {
2 void Delete(Animal animal);
3 Animal Find(int? id);
4 IList<Animal> GetAll();
5 void Insert(Animal animal);
6 void Update(Animal animal);

Inside the controller, we have two constructors. One to help us for this example which instantiate the service layer and the real one that takes a single parameter. This is the one that you should have in your enterprise grade software because it can inject any thing of IAnimalService into the controller.

1public class AnimalController : Controller {
2 private IAnimalService_service;
4 public AnimalController(IAnimalService animalService) {
5 _service = animalService;
6 }
7 //...

Before implementing the unit of work, we will create a new repository to illustrate why the unit of work is required. We will also do a little re-factoring by changing the repository to stop having automatically a call to SaveChanges. This allow us to insert several entities in a single transaction.

This is now the animal service class and interface.

1public interface IAnimalService {
2 void Delete(Animal animal);
3 void Delete(IList<Animal> animals);
4 Animal Find(int? id); IList<Animal> GetAll();
5 void Save(Animal animal);
6 void Save(IList<Animal> animal);
9public class AnimalService: IAnimalService {
10 private IAnimalRepository animalRepository;
12 public AnimalService(IAnimalRepository animalRepository) {
13 this.animalRepository = animalRepository;
14 }
16 public Models.Animal Find(int? id) {
17 return this.animalRepository.Find(id);
18 }
20 public void Delete(IList<Animal> animals) {
21 foreach (var animal in animals) {
22 this.animalRepository.Delete(animal);
23 }
25 this.animalRepository.Save();
26 }
28 public void Delete(Models.Animal animal) {
29 this.Delete(new List<Animal> { animal });
30 }
32 public IList<Animal> GetAll() {
33 return this.animalRepository.GetAll();
34 }
36 public void Save(Animal animal) {
37 Save(new List<Animal> { animal });
38 }
40 public void Save(IList<Animal> animals) {
41 foreach (var animal in animals) {
42 if (animal.Id == default(int)) {
43 this.animalRepository.Insert(animal);
44 } else {
45 this.animalRepository.Update(animal);
46 }
47 }
49 this.animalRepository.Save();
50 }

As you can see, it's better. It also hide the complexity for update and insert by having a single method "save". Next, we will create a new repository. We won't code its detail but we will use it inside the AnimalService to simulate a case where we need to interact on several entities.

1public class HumanRepository : IHumanRepository { }
2public interface IHumanRepository {
3 void Insert(Models.Human humain);

We also need to modify the service to have in its constructor the IHumanRepository.

1public class AnimalService: IAnimalService {
2 private IAnimalRepository animalRepository;
3 private IHumanRepository humanRepository;
5 public AnimalService(IAnimalRepository animalRepository, IHumanRepository humanRepository) {
6 this.animalRepository = animalRepository;
7 this.humanRepository = humanRepository;
8 }
9 //...

Then we can simulate the need to have something in the same transaction between animal and human repository. This can be in the Save method of the AnimalService. Let's create a new save method in the service which take an Animal and also an Human. In IAnimalService we add.

1void SaveAll(Animal animal, Human humain);

And in the concrete implementation we have :

1public void SaveAll(Animal animal, Human humain) {
2 his.animalRepository.Insert(animal);
3 this.humanRepository.Insert(humain);

This is where the unit of work is required. The animal repository has its own DbContext and the human repository has its one two. Since both have not the same repository, they are in two different transaction. We could wrap these both lines with a TransactionScope but since Entity Framework is already a transaction scope and since in more complex scenario where we would want to use the DbContext furthermore, having to use the same DbContext is something viable.

Implementing Unit of Work pattern

As we have seen, we need to share the DbContext. This is where the unit of work shines. The first move is to create the unit of work which hold the DbContext.

1public interface IUnitOfWork { IDbSet<T> Set<T>() where T:class;
3DbEntityEntry<T> Entry<T>(T entity) where T:class;
5void SaveChanges(); }

The interface could be richer but this should be the minimal number of methods. The implementation is only having a central point for every database sets. In a more domain driven design application we could restrain entities by having a DbContext that is less general than the one created. "AllDomainContext" contains all entities set. This is perfect to create the whole database or when your application has a limited number of entities (under 50). But if you are domain driven design or with a big application, to have Entity Framework perform well and restrict the domains, having several DbContext is a good solution. With unit of work and its generic T class, you can pass any domain you want to have.

1public class UnitOfWork<T>:IUnitOfWork where T : DbContext, new() {
2 public UnitOfWork() {
3 DatabaseContext = new T();
4 }
6 private T DatabaseContext { get; set; }
8 public void SaveChanges() { DatabaseContext.SaveChanges(); }
10 public System.Data.Entity.IDbSet<T> Set<T>() where T : class { return DatabaseContext.Set<T>(); }
12 public DbEntityEntry<T> Entry<T>(T entity) where T : class { return DatabaseContext.Entry<T>(entity); }

This unit of work is very general since it can takes T as set. This mean that any entity defined can be used. In our example, with this modified unit of work, the controller needs to be changed too.

1public class AnimalController : Controller {
2 private IAnimalService_service;
4 public AnimalController() {
5 var uow = new UnitOfWork<AllDomainContext>();
6 _service = new AnimalService(uow, new AnimalRepository(uow), new HumanRepository(uow));
7 }
9 public AnimalController(IAnimalService animalService) {
10 _service = animalService;
11 }
12 //...

So, the unit of work is instantiated with the domain we want. Here, it's everything. We still have the "real" constructor that takes only the IAnimalService which is the one that should be used in the real application with inversion of control to inject the controller. Since it's an article, to keep it simple, I show you what the IoC should do in the background.

The animal service is changed too to work with the unit of work.

1public class AnimalService: IAnimalService {
2 private IAnimalRepository animalRepository;
3 private IHumanRepository humanRepository;
4 private IUnitOfWork unitOfWork;
5 public AnimalService(IUnitOfWork unitOfWork, IAnimalRepository animalRepository, IHumanRepository humanRepository) {
6 this.unitOfWork = unitOfWork;
7 this.animalRepository = animalRepository;
8 this.humanRepository = humanRepository;
9 }
11 public Animal Find(int? id) { return this.animalRepository.Find(id); }
13 public void Delete(IList<Animal> animals) {
14 foreach (var animal in animals) {
15 this.animalRepository.Delete(animal);
16 }
18 this.unitOfWork.SaveChanges();
19 }
21 public void Delete(Models.Animal animal) { this.Delete(new List<Animal> { animal }); }
23 public IList<Animal> GetAll() { return this.animalRepository.GetAll(); }
25 public void Save(Animal animal) { Save(new List<Animal> { animal }); }
27 public void Save(IList<Animal> animals) {
28 foreach (var animal in animals) {
29 if (animal.Id == default(int)) {
30 this.animalRepository.Insert(animal);
31 } else {
32 this.animalRepository.Update(animal);
33 }
34 }
36 this.unitOfWork.SaveChanges();
37 }
39 public void SaveAll(Animal animal, Human humain) {
40 this.animalRepository.Insert(animal);
41 this.humanRepository.Insert(humain);
42 this.unitOfWork.SaveChanges();
43 }

The repository now accepts the unit of work. It can works with set defined in the domain without problem.

1public class AnimalRepository : WebsiteForUnitOfWork.DataAccessLayer.Repositories.IAnimalRepository {
2 private IUnitOfWork UnitOfWork { get; set; }
4 public AnimalRepository(IUnitOfWork unitOfWork) { this.UnitOfWork = unitOfWork; }
6 public Models.Animal Find(int? id) { return UnitOfWork.Set<Animal>().Find(id); }
8 public void Insert(Models.Animal animal) { UnitOfWork.Set<Animal>().Add(animal); }
10 public void Update(Models.Animal animal) { UnitOfWork.Entry(animal).State = EntityState.Modified; }
12 public void Delete(Models.Animal animal) { UnitOfWork.Set<Animal>().Remove(animal); }
14 public IList<Animal> GetAll() { return UnitOfWork.Set<Animal>().AsNoTracking().ToList(); }

It's possible to continue to improve the unit of work and Entity Framework by going further in the use of the repository. But, what have been shown here is enterprise graded repository design. It allows you to divide the domain and improve the performance of Entity Framework by the same time. It allows to have an abstraction between the Asp.Net MVC front and the Entity Framework. It easily testable because we use interface which can be mocked easily. Benefits are clear but the price to pay is the overwhelm required to support this infrastructure. More classes need to be in place. Still, the version presented is light and once the setup is done, adding new entity is only a matter of editing the context in which it belongs and create into the repository what action is needed.

Source code

You can find the source code on GitHub for this Unit of work example.