A Better Entity Framework Unit Of Work Pattern
The standard Unit Of Work pattern has been around for the last 5 years. Today, I talk about a better way to implement the Unit of Work design pattern.
When Entity Framework entered the picture some years ago, I was almost completed with an implementation of my own ORM (Object-Relational Mapper). The only thing missing in my ORM was a caching mechanism to speed it along.
Once Entity Framework jumped from version 1 to 4, I decided to give it a try.
After using EF4 for a while, I got into it and started using it more and more. At that point, I dropped work on my own ORM to start using EF4 exclusively. I came to appreciate the framework and the EF team's efforts making it better over the years.
However, I was always worried about all of these repositories scattered around my application and started using the Unit Of Work design pattern.
As with most developers, I wanted to make myself better and find out the best practices for the Entity Framework Unit of Work design pattern so I started looking around the big, bad web and found the Microsoft Unit of Work pattern for ASP.NET MVC.
I also found a lot of developers using this particular method.
public class MyUnitOfWork { private readonly EFContext _context; private PostRepository _postRepository; private TagRepository _tagRepository; public MyUnitOfWork() : this(new EFContext()) { } public MyUnitOfWork(EFContext context) { _context = context; } public PostRepository PostRepository { get { return _postRepository ?? (_postRepository = new PostRepository(_context)); } } public TagRepository TagRepository { get { return _tagRepository ?? (_tagRepository = new TagRepository(_context)); } } }
On top of that, I saw an exceptional post from Derek Greer on Los Techies about him conducting a Survey of Entity Framework Unit of Work Patterns.
So like Mr. Greer, this got me thinking of a better way to create a better Unit of Work.
Two SOLID Rules Broken
After going through his post, I started thinking of different ways to improve the Unit of Work pattern using Dependency Injection.
The points he mentioned in the post are perfectly valid reasons to avoid this particular pattern. I'm hoping I can address all of these points and come up with a better pattern by the end of this post.
Point 1
This approach leads to opaque dependencies.
How true! All dependencies are explicitly defined in the Unit Of Work...hard-coded. Not a great pattern or easy to extend.
Point 2
This violates the Open/Closed principle.
Again, if you want to extend the Unit of Work class by adding another repository, you need to write it into the Unit of Work class.
The open/closed principle says code should be "open for extension, closed for modification."
Point 3
This violates the Single Responsibility Principle.
I understand his approach, but it does encapsulate the committing and rollback of transactions through the DbContext.
Also, according to Martin Fowler, a unit of work "maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems."
The "list of objects" are the repositories, the "business transactions" are the repository-specific business rules to retrieve data, and the "coordination of the writing of changes and concurrency problems" are through the DbContext.
Point 4
This approach uses nominal abstraction which is semantically coupled with Entity Framework.
When developers use Entity Framework, they create entities using a T4 template or the Entity Framework Reverse POCO Code First Extension. Once these entities are built, we have to inherit from the generated context to make sure any future changes aren't lost.
I understand that defining a specific DbContext in the Unit of Work is required for it to work properly, but I think that's the nature of the beast. If you want to use a blogging module/context, you have to access the Blog Module.
Any thoughts on this?
Points Taken!
I've read this post over a number of times and think it's definitely worth it to examine the different approaches and see which one makes sense for you or your company.
After some thought, my first attempt is listed below using Dependency Injection. I'm using Ninject for this particular example, but you can insert your favorite DI library here. The GetRepository<T> method is simple enough to use your own DI library.
Here's my first cut at this.
public class MyBlogUnitOfWork { private readonly EFContext _context; public MyBlogUnitOfWork() : this(new EFContext()) { } public MyBlogUnitOfWork(EFContext context) { _context = context; } public T GetRepository<T>() where T : class { using (var kernel = new StandardKernel()) { kernel.Load(Assembly.GetExecutingAssembly()); var result = kernel.Get<T>(new ConstructorArgument("context", _context)); // Requirements // - Must be in this assembly // - Must implement a specific interface (i.e. IBlogModule) if (result != null && result.GetType().GetInterfaces().Contains(typeof(IBlogModule))) { return result; } } // Optional: return an error instead of a null? //var msg = typeof (T).FullName + " doesn't implement the IBlogModule."; //throw new Exception(msg); return null; } public void Commit() { _context.SaveChanges(); } public void Rollback() { _context .ChangeTracker .Entries() .ToList() .ForEach(x => x.Reload()); } public void Dispose() { if (_context != null) { _context.Dispose(); } } }
The GetRepository<T> method is meant to return a repository that implements an IBlogModule in the current project/assembly.
When I reflected on this approach, I came up with some thoughts on why this design makes sense:
- When using dependency injection, there may be an overlap of multiple unit of work patterns in other assemblies. If I decided to use another method instead of the GetExecutingAssembly() and grab assemblies by extension or directory, I need a way to receive the correct repository I requested. If you request a UserRepository and it's in two projects, you could receive a different repository than expected. So based on this particular unit of work, all repositories implement an IBlogModule in this assembly. That way, I know that I'm grabbing the right repository from the right assembly. If it implements an IBlogModule, then I can use it.
- Besides the method name, there is no mention of a repository in this Unit of Work pattern at all. This solves the Open/Closed principle. If I want to add another repository, my new repository would implement the IBlogModule interface and my unit of work would not require any changes.
- I have commit and rollback methods in my unit of work based on the context used, but we could add TransactionScope as well as Mr. Greer mentioned in his post.
- Keep in mind the IBlogModule doesn't have ANY methods or properties attached to it. It's strictly a flag for making our DI library aware that we "new up" the right repository.
Unfortunately, it does use an Entity Framework-specific DbContext in this assembly (EFContext) so it's definitely coupled with this business assembly. Does that make it a bad design? Would I have any other use for my BlogContext in another unit of work somewhere in another assembly?
I haven't found one yet.
How to use it
If you want to use this particular Unit of Work, an example is below:
var unitOfWork = new BlogUnitOfWork(); var repository = unitOfWork.GetRepository<PostRepository>(); repository.Add(post); unitOfWork.Commit();
While you do need to be specific in which repository you want to use, it makes your Unit of Work a little smaller and more manageable.
Conclusion
This post was meant to examine the Entity Framework's Unit of Work design pattern and come up with a better way to make the pattern easier to work with and adhere to some SOLID principles.
I want to thank Derek Greer for writing such a great post. If it wasn't for that post, I wouldn't have given the Unit of Work a second glance.
If you think I've violated some principles in my code, please let me know. I always want to make code better than what it is. Post your comments and questions below.