The Repository pattern and Unit of Work pattern are used together most of the time. Therefore I will combine them in this post and show how to implement them both.
The Repository mediates between the domain and data mapping layers, acting like an in-memory collection of domain objects. (“Patterns of Enterprise Application Architecture” by Martin Fowler)
- Decouple Business code from data Access. As a result, the persistence Framework can be changed without a great effort
- Separation of Concerns
- Minimize duplicate query logic
The Repository pattern is often used when an application performs data access operations. These operations can be on a database, Web Service or file storage. The repository encapsulates These operations so that it doesn’t matter to the business logic where the operations are performed. For example, the business logic performs the method GetAllCustomers() and expects to get all available customers. The application doesn’t care whether they are loaded from a database or web service.
The repository should look like an in-memory collection and should have generic methods like Add, Remove or FindById. With such generic methods, the repository can be easily reused in different applications.
Additionally to the generic repository, one or more specific repositories, which inherit from the generic repository, are implemented. These specialized repositories have methods which are needed by the application. For example, if the application is working with customers the CustomerRepository might have a method GetCustomersWithHighestRevenue.
With the data access set up, I need a way to keep track of the changes. Therefore I use the Unit of Work pattern.
Maintains a list of objects affected by a business transaction and coordinates the writing out of changes. (“Patterns of Enterprise Application Architecture” by Martin Fowler)
- Increases the level of abstraction and keep business logic free of data access code
- Increased maintainability, flexibility and testability
- More classes and interfaces but less duplicated code
- The business logic is further away from the data because the repository abstracts the infrastructure. This has the effect that it might be harder to optimize certain operations which are performed against the data source.
Entity Framework has a DbSet class which has Add and Remove method and therefore looks like a repository. the DbContext class has the method SaveChanges and so looks like the unit of work. Therefore I thought that it is possible to use entity framework and have all the Advantages of the Repository and Unit of Work pattern out of the box. After taking a deeper look, I realized that that’s not the case.
The problem with DbSet is that its Linq statements are often big queries which are repeated all over the code. This makes to code harder to read and harder to change. Therefore it does not replace a repository.
The problem when using DbContext is that the code is tightly coupled to entity framework and therefore is really hard, if not impossible to replace it if needed.
The Repository pattern consists of one IRepository which contains all generic operations like Add or Remove. It is implemented by the Repository and by all IConcreteRepository interfaces. Every IConcreteRepository interface is implemented by one ConcreteRepository class which also derives from the Repository class. With this implementation, the ConcreteRepositoy has all generic methods and also the methods for the specific class. As an example: the CustomerRepository could implement a method which is called GetAllSeniorCustomers or GetBestCustomersByRevenue.
The unit of work provides the ability to save the changes to the storage (whatever this storage is). The IUnitOfWork interface has a method for saving which is often called Complete and every concrete repository as property. For example, if I have the repository ICustomerRepository then the IUnitOfWork has an ICustomerRepositry property with a getter only. Additionally, IUnitOfWork inherits from IDisposable.
The UnitOfWork class implements the Complete method, in which the data get saved to the data storage. The advantage of this implementation is that wherever you want to save something you only have to call the Complete method from UnitOfWork and don’t care about where it gets saved.
For this example, I created a console project (RepositoryAndUnitOfWork) and a class library (RepositoryAndUnitOfWork.DataAccess). In the class library, I generate a database with a customer table.
Next, I let Entity Framework generate the data model from the database. If you don’t know how to do that, check the documentation for a step by step walkthrough.
After setting up the database, it’s time to implement the repository. To do that, I create a new folder, Repositories, in the class library project and add a new interface IRepositry. In this Interface, I add all generic methods I want to use later in my applications. These methods are, GetById, Add, AddRange, Remove or Find. To make the Interface usable for all classes I use the generic type parameter T, where T is a class.
After the generic repository, I also implement a specific repository for the customer. The ICustomerRepository inherits from IRepository and only implements one method.
After implementing all interfaces it’s time to implement concrete repository classes. First, I create a class Repository which inherits from IRepository. In this class, I implement all methods from the interface. Additionally to the methods, I have a constructor which takes a DbContext as Parameter. This DbContext instantiates a DbSet which will be used to get or add data.
The implementations of the methods are pretty straight Forward. The only interesting one might be the Find method which takes an expression as parameter. In the implementation, I use Where to find all entries which fit the Expression of the parameter.
The final step for the Repository pattern is to implement the CustomerReposiotry. This class derives from Repository and ICustomerRepository and implements the method from the interface. The constructor takes a CustomerDbEntities object as Parameter which is derived from DbContext and generated by Entity Framework.
All repositories are created now, but I need a class which writes my data to the database, the unit of work. To implement this class, I first implement the IUnitOfWork interface in the repositories folder in the library project. This interface derives from IDisposable and has an ICustomerRepository property and the method Complete. This method is responsible for saving changes. The Name of the method could be Save, Finish or whatever you like best.
Like before, I add the concrete implementation of IUnitOfWork to the repositories folder in the console application project. The constructor takes a CustomerDbEnties object as parameter and also initializes the ICustomerRepository. The Complete Method saves the context with SaveChanges and the Dispose method disposes changes.
The usage of the unit of work differs between a web application and a console application. In an MVC application, the unit of work gets injected into the constructor. In the console application, I have to use a using statement. I can use with unitOfWork.Customer.Method(), for example unitOfWork.GetBestCustomers(3). To save the changes use unitOfWork.Complete().
You can find the source code on GitHub. If you want to try out the example, you have to change to connection string in the App.config to the location of the database on your computer
Note: In this example, I always talked about writing and reading data from the database. The storage location could also be a web service or file drive. If you want to try my example, you have to change the connection string for the database in the App.config file to the Location of the database on your computer.
In this post, I showed how to implement the Repository and Unit of Work pattern. Implementing both patterns results in more classes but the advantages of abstraction increased testability and increased maintainability outweigh the disadvantages. I also talked about entity framework and that although it looks like an out of the box Repository and Unit of Work pattern, it comes at the cost of tight coupling to the framework and should not be used to replace the patterns.