Repository Implementations
In my previous post I have presented a way to separate your data access from the business logic, when a relational database is used. I have shown another implementation of the well-known Repository pattern. Since Martin Fowler described it in his book Patterns of Enterprise Application Architecture it became one
In my previous post I have presented a way to separate your data access from the business logic, when a relational database is used. I have shown another implementation of the well-known Repository pattern. Since Martin Fowler described it in his book Patterns of Enterprise Application Architecture it became one of the most implemented pattern in enterprise applications. As it happens with all the design patterns, design principles or any other widely known best practice, we find them in many different implementations, mixed in different ways, but they all have identical things, because they all implement the same solution to a common problem. They all have classes and interfaces named the same. Things like: Repository
, UnitOfWork
, IEntity
, BaseEntity
, ConcurencyException
and so on.
I like the quote of Christopher Alexander in GoF (originally in his book A Pattern Language):
Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice
In most of the projects I’ve been involved in, I’ve seen the Repository pattern implemented differently. Tweaked to the context of that project, to the specifics of that team, according with their own style. All different implementations of the same patterns and practices.
Out of the curiosity of how many other blog posts are out there, which show similar implementation with the one I’ve presented, I have googled: “generic repository implementation”. As expected there are quite a few, and as expected they are very similar with mine and with each other. I have randomly picked a few (the first 5 returned by my google search at the time of writing this article) that I will comment in this post.
Blog Post: Implement Step-by-Step Generic Repository Pattern in C#
I like the idea of defining an IEntity
interface which should be implemented by all the entities that your repository can persist. This opens the possibility of writing a more generic code into a default implementation of IRepository<T>
, (a Repository<T>
class), where you could push all the common code like a generic implementation of .FindById(int Id)
. This will only work if you can enforce the convention that all the tables in your database have a surrogate primary key of type integer.
I appreciate a lot the discussions in the comments of the post. I am closer to the point of view of ardalis regarding the use of such abstraction on top of EF. However, I don’t see IQueriable<T>
as a leaky abstraction. In my view it abstracts a query, which may be executed on different query providers, EF being one of them. It also helps a lot when Unit Testing, because I can stub the IRepository
to return array.AsQueriable()
.
The implementation in this blog post also has the IEntity
, but with a slight variation. It is generic by the type of the primary key. With this, we can have different types for the primary key, but it increases the complexity of writing generic code in a common repository implementation.
I like the IAuditableEntity
, which should be implemented by all entities on which we want to store information like CreatedBy
, CreatedDate
etc. With this, in a generic implementation of the repository before we save an entity we can check if it implements IAuditableEntity
and if yes, then we can set the correct values in CreatedBy
, CreatedData
properties and then continue with the save.
The implementation of the UnitOfWork
looks a bit strange. It has a Commit()
function, which should be called when the client wants to persist a set of changes. What I don’t like is that the client service will need to use one instance of a Repository
, another instance of an UnitOfWork
and these instances should be built in such way that they wrap the same DbContext
instance. The way objects are created (Dependency Injection, Service Locator or custom factories), needs to take care of this, and all the client services need to be aware of it and use it consistently. Things may get too complex when a client service will need to use repositories of two different entities. It will have to deal with four dependencies only for this.
This implementation goes even further and defines a generic service for maintaining business entities. It is called EntityService
. A suggestive name I’ve also used in different projects, for a similar generic service with the same scope. These set of services should use the repository (data access), encapsulate the logic around maintaining a business entity (business logic) and give to the controller (UI) functions for the basic CRUD operations.
This blog post addresses the UnitOfWork
implementation in a better way than the previous two. Here, the UnitOfWork
has set of Repository
instances that it creates and makes them available through get properties. This solves in a better way the thing that I didn’t like in the previous post. The DbContext
is created and owned by the UnitOfWork
, and because it is the one that creates the repositories it may pass it to the Repository
class.
The drawback of this is that for a client class is quite difficult to know which are the repositories that are available on a specific UnitOfWork
instance. It does not know what get properties to expect from an UnitOfWork
. This may lead to inconsistency. As a client I might end up reaching the entities I want through strange paths starting from different repositories. It may also lead developers to add repository properties to UnitOfWork
classes as they need them in different context, making the UnitOfWork
classes to get fat and difficult to maintain.
The generic UnitOfWork
class presented in the article helps, but even that may become quite complex when there are many specific repository implementations that need to be created and cached in the dictionary. Mainly the UnitOfWork
turns into a Service Locator implementation for repositories of different entity types.
This implementation has the same way of implementing a UnitOfWork
as the previous. The UnitOfWork
has a generic repository through a get property.
The article shows the idea of abstracting the DbContext
under an IDbContext
interface. With this, the UnitOfWork
and Repository
no longer depend on a specific context, but on this abstraction. This can help in separating the data access implementation in another assembly than the one in which we have generated the code for a specific context. This interface may be useful when you want that Dependency Injection or Service Locator to get directly involved on creating context instances and you need an interface for configuring the container. Otherwise, the same separation may be achieved with an abstract factory.
Another interesting thing is the RepositoryQuery<T>
. The Repository.Get()
does not return an IEnumerable
nor an IQueryable
. It returns this RepositoryQuery<T>
which is somehow in the middle. It limits the queries which may be defined, by giving Include
, Filter
or OrderBy
functions and when you say .Get()
it composes the expressions and executes the query on the underlying repository.
It is hard to see the cases where the complexity added by this extra indirection pays off. It brings this common place where we can make sure that the queries written by upper layers may be adapted to work the same on any query provider, but unless we know that we need to easily replace EF with something else… might not worth doing it. Another advantage, I can think of, is that it enforces consistency in the client queries, but this needs to be balanced with the limitations it brings.
Blog Post: Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application
This article is part of a tutorial on www.asp.net about getting started with EF and MVC. It presents the step by step the process of evolving from a simple repository, which does nothing more than wrapping a DbContext
, towards a generic repository and then towards a unit of work that has more repository instances. It follows the same idea. An UnitOfWork
class which provides a generic repository through a get property. The UnitOfWork
takes care of creating and caching multiple repositories instances, one for each entity type. This may turn it into a complex factory.
Here, the Get()
function of the repository returns an IEnumerable
. It receives through input parameters different parts of a query. There are parameters to give a filter expression, an order by expression or a string to specify the related entities. In my view this is an unfortunate choice. It does not give to the client a fluent API to specify the query. The client code has to tweak the parameters in different ways which increases the complexity on its side. There will also be cases when new Get()
overloads are needed to write in them the LINQ queries directly against the DbContext
. This may lead to inconsistency. I would rather return IQueriable
, or if not then have a Get()
without parameters which returns IEnumeralble
and explicitly ask for specific repository implementations which define other Get()
overloads for the specific queries.
Conclusions
I think the biggest take away from these blog posts is that there are many ways we can implement the repository and unit of work patterns. All implementations will achieve the same goal which is separating data access concern from the other concerns. The implementations differ because they are used in different context. All have pluses and minuses. What may work in one project, may not work in other, because the tradeoffs that are made differ from project to project. What is a critical plus for some teams in some contexts, may not be useful for others. What is not a big minus in some cases may prove to ruin projects in other contexts.
The more implementations of a pattern you see, the more design ideas you will get when you need to implement it for your current case. The more projects you’ve done where such patterns were used, the easier and faster it will be to implement it again for a new context or to evaluate possible implementations.
Many implementations of data access are discussed in detail in my Code Design Training
Featured image credit: 1tjf via 123RF Stock Photo
We share our insights and experiences from working on client projects and teaching developers.
Our articles blend technical expertise with real-world challenges, offering a unique perspective on Code Design.
For us, Code Design means structuring code to ensure predictability and making it easy to manage and adapt long after it's written.