My very first article is revolving arround ASP.NET MVC and the correct use of UnitOfWork / Repository patterns.
This article is not about how to plumb a dependency injection (DI) container in ASP.NET MVC, nor about teaching the hows&to of DI.
I am assuming you are already familiar with Inversion of Control (IoC) / DI and that you know how to properly use your favorite DI container in various frameworks (ASP.NET MVC being one).
I am assuming you are already familiar with Inversion of Control (IoC) / DI and that you know how to properly use your favorite DI container in various frameworks (ASP.NET MVC being one).
This article is also not discussing how to use Entity Framework code first, nor ASP.NET MVC (you’ll find plenty of books and articles on the subject).
This article is all about designing a nice domain layer based on Unit of Work and Repository patterns, while keeping it as simple as possible (embracing KISS) yet powerfull and fully loose coupled.
The usage of this layer will be illustrated through Entity Framework Code First as the provider and ASP.NET MVC as the client, even though both of these could be replaced by other components (that’s one of the point of loose coupling after all !)
Most of the ASP.NET MVC articles/books I have read on ASP.NET MVC are presenting basic designs to access the data layer through repositories that are looking nice at first sight, but are truly not viable designs.
Let’s start this article by looking at such a poor design and why it is “wrong”.
A wrong (alas common) design
The most common “anti-pattern” (I used it too) is to use an interface PER repository type (such as IOrderRepository, ICustomerRepository, IEmployeeRepository .. and so on), and to inject (via constructor injection) concrete repository implementations into the controller.
This is a totally functional loose coupled design relying on DI, allowing different repository implementations per provider, easy unit testing and globally working fine … however if you scratch the surface of this design a little bit, you’ll start to see quite a few ‘glitches’ , here are the main ones :
Each repository requires : one interface + one concrete class per “provider” (for example “entity framework” or “in memory”)
If you are starting to have quite some repositories, it means that you will have to type (or copy paste arround) a lot of code for creating the new associated types and probably many files created in the way.
If you have “custom” specialized methods for each repository, it also means implementing them for all the providers.
If you have “custom” specialized methods for each repository, it also means implementing them for all the providers.
The joy of never ending constructor parameters
One abstract type per repository equals one parameter in your controller constructor per repository that needs to be accessed by the controller. If a controller is performing operations on multiple repositories, this can quickly become a mess with a lot of parameters in the constructor (even if constructor will never be called explicitly by your code but by the DI container -constructor injection- it still looks kind of crapy).
Moreover if you need to add access to a new repository in a controller, it means adding a new parameter to the constructor and doing a new binding in your DI container.
Moreover if you need to add access to a new repository in a controller, it means adding a new parameter to the constructor and doing a new binding in your DI container.
Unit of Work usage is nonexistent (or rather completely wrong)
By injecting repositories individually in each controller, the true power of the unit of work pattern is completely bypassed.
Indeed, through this basic design, to illustrate via EF Code First, a DbContext (EF UnitOfWork) is usually instantiated per repository and “at best” a Commit method is present in each repository to call SaveChanges on the DbContext (and apply all modifications to DB) once all operations have been done on the repository. At worse the call to SaveChanges on the DbContext is done in each repository method performing modifications in the repository.
Indeed, through this basic design, to illustrate via EF Code First, a DbContext (EF UnitOfWork) is usually instantiated per repository and “at best” a Commit method is present in each repository to call SaveChanges on the DbContext (and apply all modifications to DB) once all operations have been done on the repository. At worse the call to SaveChanges on the DbContext is done in each repository method performing modifications in the repository.
You are using a nice loose coupled design, playing nicely with dependency injection and unit testing but you are shooting yourself a bullet in the head by not correctly using the UnitOfWork pattern.
The main problem with this incorrect use of the unit of work pattern is that for a specific user request, triggering call to action method and potentially accessing multiple repositories, doing work on them, you are creating multiple unit of works whereas a single unit of work should be used !The definition of the Unit Of Work pattern is rather clear : “Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.” (http://martinfowler.com/eaaCatalog/unitOfWork.html).The business transaction here is typically triggered by the end user, indirectly calling an action method. It starts when the end-user triggers the operation, and ends when the operation is completed, whatever the number of repositories accessed and the number of CRUD operations performed on them. This means that a single unit of work should be used in the context of the operation/transaction (the request) and not many different ones.Typically, to use Entity Framework as a provider example, following this bad design would result in calling SaveChanges multiple times, meaning multiple round trips to DB through multiple transactions which is typically not the behavior wanted (and absolutely not the Unit Of Work philosophy).
Apart from the performance aspect, it also leads to a problem when an error/exception happens in the middle of an operation in an action method. If you already made some changes in some repositories and commited the changes, but the global operation is not complete (potentially other repositories should have been updated as well but have not been), it will leave your persisted data part of the operation in an incoherent state (I wish you good luck to rollback each changes). Whereas if you only use a single UnitOfWork for the operation, if it fails before completing (before reaching the end of the action method), then no data is updated at all part of the operation, your data store stays clean (and it also does a single round trip to the DB, in a single transaction for changes done accross all repositories).
A much better design …
I went through quite some different approaches and improvements to make my domain layer based on unit of work and repository as clean/simple as possible with a minimum number of types/files, yet fully loose coupled and extensible.
I will spare you with all the various intermediates steps and I am directly going to discuss the “final” (current) design :
First of all I define an abstract generic repository type, containing very basic atomic operations:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| public interface IGenericRepository<T> : where T : class { IQueryable<T> AsQueryable(); IEnumerable<T> GetAll(); IEnumerable<T> Find(Expression<Func<T, bool >> predicate); T Single(Expression<Func<T, bool >> predicate); T SingleOrDefault(Expression<Func<T, bool >> predicate); T First(Expression<Func<T, bool >> predicate); T GetById( int id); void Add(T entity); void Delete(T entity); void Attach(T entity); } |
I then define an abstract unit of work type, containing all the generic repositories being part of the unit of work, along with a single Commit() method used to persist all changes done in the repositories to the underlying data store (please note that Order, Customer and Employee are pure POCO classes).
1
2
3
4
5
6
7
8
| public interface IUnitOfWork : IDisposable { IGenericRepository<Order> OrderRepository { get ; } IGenericRepository<Customer> CustomerRepository { get ; } IGenericRepository<Employee> EmployeeRepository { get ; } void Commit(); } |
Now for each data provider we need to make a single concrete implementation of IUnitOfWork and IGenericRepository whatever the number of repositories and we are good to go !
I will illustrate implementating these interfaces, through implementations targeting Entity Framework (code first).
First of all EfUnitOfWork.
This class is implementing IUnitOfWork and also inherits from DbContext (EF Code First unit of work).
It contains DbSets, which can be seen as repositories from EF point of view (in fact in EF Code First, you can substitute in your mind the word “Unit Of Work” with “DbContext” and “Repository” with “DbSet”).
The constructor just instanciate all the repositories by passing them the corresponding DbSet in their constructor. This can be improved by instantiating repositories only when they are accessed. Indeed if your unit of work contains 20 repositories and your controller is just going to use one, this is a lot of useless instantiations.
This class is implementing IUnitOfWork and also inherits from DbContext (EF Code First unit of work).
It contains DbSets, which can be seen as repositories from EF point of view (in fact in EF Code First, you can substitute in your mind the word “Unit Of Work” with “DbContext” and “Repository” with “DbSet”).
The constructor just instanciate all the repositories by passing them the corresponding DbSet in their constructor. This can be improved by instantiating repositories only when they are accessed. Indeed if your unit of work contains 20 repositories and your controller is just going to use one, this is a lot of useless instantiations.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| public class EfUnitOfWork : DbContext, IUnitOfWork { private readonly EfGenericRepository<Order> _OrderRepo; private readonly EfGenericRepository<Customer> _customerRepo; private readonly EfGenericRepository<Employee> _employeeRepo; public DbSet<Order> Orders { get ; set ; } public DbSet<Customer> Customers { get ; set ; } public DbSet<Employee> Employees { get ; set ; } public EfUnitOfWork() { _orderRepo = new EfGenericRepository<Order>(Orders); _customerRepo = new EfGenericRepository<Customer>(Customers); _employeeRepo = new EfGenericRepository<Employee>(Employees); } #region IUnitOfWork Implementation public IGenericRepository<Order> OrderRepository { get { return _orderRepo; } } public IGenericRepository<Customer> CustomerRepository { get { return _customerRepo; } } public IGenericRepository<Employee> EmployeeRepository { get { return _employeeRepo; } } public void Commit() { this .SaveChanges(); } #endregion } |
Then the class implementing the abstract generic repository, which just delegates all calls to the associated Entity Framework DbSet :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| public class EfGenericRepository<T> : IGenericRepository<T> where T : class { private readonly DbSet<T>_dbSet; public EfGenericRepository(DbSet<T> dbSet) { _dbSet = dbSet; } #region IGenericRepository<T> implementation public virtual IQueryable<T> AsQueryable() { return _dbSet.AsQueryable(); } public IEnumerable<T> GetAll() { return _dbSet; } public IEnumerable<T> Find(Expression<Func<T, bool >> predicate) { return _dbSet.Where(predicate); } // And so on ... #endregion } |
That’s it ! Now if you need to implement another provider, let’s say “InMemory” (usefull for unit testing) all you have to do is to create two other classes InMemGenericRepository and InMemUnitOfWork using a HashSet for example as the data store.
That looks much better that the original “anti pattern” design now !
… And it’s usage
Let’s say your HomeController needs to access all three different repositories.
With the old design, your HomeController constructor signature would have looked as
With the old design, your HomeController constructor signature would have looked as
1
| public HomeController(IOrderRepository orderRepo, ICustomerRepository customerRepo, IEmployeeRepository employeeRepo) |
And of course new repository to be used by HomeController, means new parameter + new private field + new binding in DI container.
With the improved design, now the signature looks much cleaner
1
| public HomeController(IUnitOfWork unitOfWork) |
And the good point is that if we add new repositories that we want to use in the controller we don’t have to change anything in the controller class, nor bindings in DI container. We can just use the new repository directly from the controller through the unit of work.
No more thinking about which repositories should the controller have access too and customizing the constructor as such. Now all controllers constructors needing to access repositories just need a single unitOfWork parameter.
No more thinking about which repositories should the controller have access too and customizing the constructor as such. Now all controllers constructors needing to access repositories just need a single unitOfWork parameter.
On DI container side, all that is needed is to bind the abstract IUnitOfWork to the desired provider implementation (EF, InMem, other …). You’ll also want to make sure that the dependency is created in a “per request scope” (considering your DI container allow this), meaning that a single unit of work will be instanciated per request and not each time the decency is required.
Neat ! But something is kind of missing.
Indeed, through IGenericRepository you just have access to very basic atomic operations on repositories which is not very cool and will surely go against the DRY principle sooner or later.
Let’s say you need in multiple places in your client code to run a complex query on a specific repository (IOrderRepository so be it).
Through the old design it’s relatively straightforward, you would just define a method in IOrderRepository and implement it for all the concrete providers (what a pain !).
However with the improved design we can’t add this method to IGenericRepository<T>.
Let’s say you need in multiple places in your client code to run a complex query on a specific repository (IOrderRepository so be it).
Through the old design it’s relatively straightforward, you would just define a method in IOrderRepository and implement it for all the concrete providers (what a pain !).
However with the improved design we can’t add this method to IGenericRepository<T>.
Hopefully, extension methods are coming to the rescue !
The idea is to add “specialized” methods to constructed generic IGenericRepository<> types through extension methods.
For example for an hypothetical method, taking two parameters, running a query on Order repository and returning an enumerable of orders you would do something like :
For example for an hypothetical method, taking two parameters, running a query on Order repository and returning an enumerable of orders you would do something like :
1
2
3
4
5
6
7
| public static class GwOrderRepositoryExtension { public static IEnumerable<Order> GetOrdersMatchingParams( this IGenericRepository<Order>, int param1, string param2) { // Do query here and return the result } } |
Then in your code you can call this method on the order repository easily :
1
| IEnumerable<Order> result = unitOfWork.OrderRepository.GetOrdersMatchingParams(1,"Foo"); |
The advantage compared to the old design is that all of these specialized method are acting on generic repositories and therefore do not need a different implementation per specialized provider ! A single implementation to rule them all.
You may also need to have some reusable methods that are performing operations on multiple repositories. In this context, you should just have to go through a static class containing extensions methods for IUnitOfWork, giving you access to all repositories. However make sure not to call Commit in these extensions methods (unless absolutely necessary). Commit should be triggered by the Unit of Work client (your action method in ASP.NET MVC context, at the end of the transaction (end of action method)).
Once again this design is probably not perfect, but still is much better than the typical basic approach of the problem and makes a correct use of the Unit Of Work pattern.
One of the problem with this design is that you kind of loose the injection granularity at a repository level. You can’t for example go through a repository targeting EF and at the same time another repository targeting InMem. But this is rarely needed I think and some solutions could be surely thought of.
This concludes my first article, hope you enjoyed and that it was of some help to you !
If you have any questions or remarks, feel free to leave a comment.
No comments:
Post a Comment