Why Entity Framework renders the Repository pattern obsolete?


A post here on a pattern I thought was obsolete yet I still see cropping up in projects using EF time and time again…

What is a Repository?

The repository pattern – to me – is just a form of data access gateway. We used it to provide both a form of abstraction above the details of data access, as well as to provide testability to your calling clients, e.g. services or perhaps even view models / controllers. A typical repository will have methods such as the following:-

interface IRepository
{
    T GetById(Int32 id);
    T Insert(T item);
    T Update(T item);
    T Delete(T item);
}

interface ICustomerRepository : IRepository
{
    Customer GetByName(String name);
}

And so on. You’ll probably create a Repository<T> class which does the basic CRUD work for any <T>. Each one of these repositories will delegate to an EF ObjectContext (or DbContext for newer EF versions), and they’ll offer you absolutely nothing. Allow me to explain…

Getting to EF data in Services

Let’s illustrate the two different approaches with a simple example service method that gets the first customer whose name is an arbitrary string. In terms of objects and responsibilities, the two approaches are somewhat different. Here’s the Repository version: –

public class Service
{
    private readonly ICustomerRepository customerRepository;
    public Customer GetCustomer(String customerName)
    {
        return customerRepository.GetByName(customerName);
    }
}
public class CustomerRepository : ICustomerRepository
{
    private readonly DatabaseContext context;
    public Customer GetByName(string customerName)
    {
        return context.Customers.First(c => c.Name == customerName);
    }
}

Using the Repository pattern, you generally abstract out your actual query so that your service does any “business logic” e.g. validation etc. and then orchestrates repository calls e.g. Get customer 4, Amend name, Update customer 4 etc. etc.. You’ll also invariably end up templating (which if you read my blog regularly you know I hate) your Repositories for common logic like First, Where etc.. – all these methods will just delegate onto the equivalent method on DbSet.

If you go with the approach of talking to EF directly, you enter your queries directly in your service layer. There’s no abstraction layer between the service and EF.

public class ServiceTwo
{
    private readonly DatabaseContext context;

    Customer GetCustomer(String customerName)
    {
        return context.Customers.First(c => c.Name == customerName);
    }
}

So there’s now just one class, the service, which is coupled to DatabaseContext rather than CustomerRepository; we perform the query directly in the service. Notice also that Context contains all our repositories e.g. Customers, Orders etc. as a single dependency rather than one per type. Why would we want to do this? Well, you cut out a layer of indirection, reduce the number of classes you have (i.e. the whole Repository hierarchy vs a fake DbContext + Set), making your code quicker to write as well as easier to reason about.

Aha! Surely now we can’t test out our services because we’re coupled to EF! And aren’t we violating SRP by putting our queries directly into our service? I say “no” to both.

Testability without Repository

How do we fix the first issue, that of testability? There are actually many good examples online for this, but essentially, think about this – what is DbContext? At it’s most basic, it’s a class which contains multiple properties, each implementing IDbSet<T> (notice – IDbSet, not DbSet). What is IDbSet<T>? It’s the same thing as our old friend, IRepository<T>. It contains methods to Add, Delete etc. etc., and in addition implements IQueryable<T> – so you get basically the whole LINQ query set including things like First, Single, Where etc. etc.

Because DBSet<T> implements the interface IDbSet<T>, you can write your own one which uses e.g. in-memory List<T> as a backing store instead. This way your service methods can work against in-memory lists during unit tests (easy to generate test data, easy to prove tests for), whilst going against the real DBContext at runtime. You don’t need to play around with mocking frameworks – in your unit tests you can simply generate fake data and place them into your fake DBSet lists.

I know that some people whinge about this saying “it doesn’t prove the real SQL that EF will generate; it won’t test performance etc. That’s true – however, this approach doesn’t try to solve that – what it does try to do is to remove the unnecessary IRepository layer and reduce friction, whilst improving testability – for 90% of your EF queries e.g. Where, First, GroupBy etc., this will work just fine.

Violation of SRP

This one is trickier. You ideally want to be able to reuse your queries across service methods – how do we do that if we’re writing our queries inline of the service? The answer is – be pramatic. If you have a query that is used once and once only, or a few times but is a simple Where clause – don’t bother refactoring for reuse.

If, on the other hand you have a large query that is being used in many places and is difficult to test, consider making a mockable query builder that takes in an IQueryable, composes on top of it and then returns another IQueryable back out. This allows you to create common queries yet still be flexible in their application – whilst still giving you the ability to go directly to your EF context.

Conclusion

Testability is important when writing EF-based data-driven services. However, the Repository pattern offers little when you can write your services directly against a testable EF context. You can in fact get much better testability from an service-with-an-EF-context based approach than just with a repository, as you can test out your LINQ queries against a fake context, which at least proves your query represents what you want semantically. It’s still not a 100% tested solution, because your code does not test out the EF IQueryable provider – so it’s important that you still have some form of integration and / or performance tests against your services.

19 thoughts on “Why Entity Framework renders the Repository pattern obsolete?

  1. I agree completely. Ive been trying to pinpoint why I did not feel right creating these repositories with EF. If I ever want to change my backing mechanism, just create a new implementation of my service which depends on it and DI it.

  2. Repositories have a place in DDD by DDD definition existing per aggregate roots. EF may or may not match an aggregate root, so it doesn’t render it obsolete. With that said, many people use the pattern when they don’t need to. You also reference a generic repository which many also point to being a bad pattern unless used internally in the repository.

  3. I don’t agree. One thing is to increase testability and have a loose coupling to underlying persistance technology. But you will also have one repository per aggregate root object (eg. an order can be an aggregate root, which also have order lines (which are not aggregate root), to make domain object persistance more generic. It’s also makes it much easier to manage objects, because when you save an order, it will also save your child items (which can be order lines).. You introduce an abstraction layer for have indipendence from the data layer according to the SOLID principle fo OOD

    1. Antonio

      Thanks for your comment. However, I’m not sure I follow your points. The Entity Framework context will save an entire aggregate graph in one call to SaveChanges(). You also have an abstraction layer through IDbSet which gives you the independence from the data layer.

  4. Antonio is right, you would want a repository for every entity. Using the context to query in the service provides no code re-use. I’ve worked on projects where things have been done like that and the code is never re-factored if needed more than once. Most likely it is pasted all over the place. The entity framework can work with Repository it just takes a while to research how to get it done. I’ve found that the combination of the Unit of Work pattern and Repository work very well for Entity Framework. Because EF saves all entity changes the unit of work should be responsible for saving not the entity repository. Hope this helps.

  5. I tend to agree with @Isaac. Isn’t it an oximoron to have a repo and UoW pattern implemented on top of an already implemented repo and UoW pattern? In my view, the EF DbContext and EF’s IDbSet are exactly that…

    That being said, I’ve impemented those patterns (not with EF, but with Telerik’s OpenAccess which in many respects is similar to EF) and it always seemed to me redundant. Nevertheless I do agree that abstractions are needed, but I’m now getting my grasp on CQRS as an alternative and I think it could realy prove it’s groound…

  6. Hi Isaac nice post, but what about async methods ? NoTracking ? using idbset no implements async and notracking methods

  7. @Rodrigo When this post was written, I don’t think EF had support for Async.:-) You’re right about NoTracking but you could do something with an extension method over IDbSet to achieve a similar sort of API.

    Or, now that EF is open source, perhaps contribute to the project and add it to the interface 🙂

    1. Thanks Isaac, I’m noob for contribute with ef project 😦

      You have any solution for async and notracking? extension method is one solution?

  8. But when is the ‘private readonly DatabaseContext context;’ set? When I create a new service with the default constructor? Do I create a new service for every single task or should I share it across the application?

  9. My current application is architected along the lines suggested by Isaac. In each ViewModel I create a context. In my code first domain models I create reusable linq queries which operate on property collections loaded by EF.

    For any domain model class that has queries which can/should be reused I create a separate partial class file that contains methods that take an IDbContext as a parameter.

    I created a FakeDbContext for testing. The actual IDbContext is loaded using Unity DI container.

  10. There are drawbacks to ditching the repository. First, you no longer have a way to convert an Entity to a business object. There are times when the database won’t match the business objects. If you have a repository between the business code and the EF you can map to a business object before handing it over to your code. Another downside is that the context from EF is disposable. Using statements and explicitly disposing objects aren’t the most elegant way of programming in my opinion. Using statements make code less readable. And you risk forgetting to dispose the context when you use calls to dispose. I make sure that de repositories don’t need to be disposable by having a using statement in every method.
    Lastly, I use repositories to predefine queries that are re-used. I have an application where some entities have an IsActive flag. In the repository I can Always select the entities that have it set to true. Without repository I would have to add that in every query with the chance of forgetting that. Also, when the selection criteria change, I only have to change code in one place.
    Having said that, I did notice that I now have a lot of repetitive code when mapping entities to business objects. I am not happy with and not happy without repositories.

  11. Hey Dennis. I think you’ve outlined one of the main issues with ORMs in general (and a reason why I tend to avoid them nowadays) – in a perfect world, your business objects should map against the DB via the ORM. Nine out of ten times, this simply isn’t possible, so you end up with a middle domain – the ORM model – which to my mind just serves to complicate things. These days I tend to use much simply DALs.

    Regarding re-use of queries, I did discuss that above – you can do it with extension methods and many other ways.

  12. Two points:

    If you have a Repository, you have a gatekeeper to the data. The repository can ensure the current user has access to the requested data. If you put it in the Service, that logic is duplicated all over the place and possibly missed.

    The other point is you can put database specific validation logic in a Repository. While some Services/Business Logic may have their own rules, but you’d want the max length, required, etc rules stored on the Repository itself so if another Service in a different project uses that Repository, those validation rules don’t need to be duplicated.

    1. Yep, good points. I think that I addressed reusable / repeatable bits of “data access” code in the post (although the post was written several years ago and in the fast moving world of design patterns maybe things have moved on ;). Max Length, Required etc. can (AFAIK) be put into EF directly – either through mappings with the latest EF versions and / or with attributes on the domain model which are handled implicitly by EF.

Leave a comment