.NET Core ASP.NET Core C# Entity Framework Core Patterns

The Repository Pattern is Dead If You Use Entity Framework

That is, it’s dead if you are using Entity Framework Core. If you’re still using straight ADO.NET, or even just dapper, then the repository pattern still probably makes sense for you. If you’re using Entity Framework, then you really aren’t gaining much from doing so anymore. Five years ago, you could reasonably argue that it was beneficial to use the repository pattern, or some form of abstraction, over entity framework for the following reasons:

  • Entity Framework had a history of poor performance and issues, and there were still some reasonable contenders at the time that you could successfully argue you may want to switch ORMs later on.
  • Mocking the database context was difficult, which made unit testing painful.
  • Support for anything other than SQL Server was either not provided out of box, or was unreliable at best. At the very least, it was nothing like the support that is now available for other persistence implementations. If you decided to move away from SQL Server, you were probably going to move away from Entity Framework as well.
  • Directly injecting the context was not as easy back then as it is today. .NET Core assumes that if you are using entity framework, you will be doing dependency injection, and now has built in configuration in start up for it.

There Is No Gain To The Repository Pattern

Given the above points, we have to ask what we are gaining by continuing with it. I don’t care what pattern, architecture, or technology you choose, you should always ask “What do I have to gain from this decision?” Does it make the code more maintainable? Does it reduce development time? Can I refactor more easily? If you aren’t asking these questions and dispassionately making decisions based on the answers you arrive at, then you are doing a disservice to your clients and to the software developers that have to work in your code. Lets look at the following code, which is actual code from a client I am currently working with.

public abstract class RepositoryBase<T, TRepo> : IRepositoryBase where T :
    ModelBase where TRepo : RepositoryBase<T, TRepo>
{
    protected DbContext _dbContext { get; }
    protected ILogger _logger { get; }

    protected RepositoryBase(DbContext dbContext, ILogger logger)
    {

        _dbContext = dbContext;
        _logger = logger;
    }

    public virtual async Task<List<T>> GetAllAsync()
    {
        DbSet dbSet = _dbContext.Set();
        return await dbSet.ToListAsync();
    }

    public virtual async Task<List<T>> GetManyAsync(List<Guid>> ids)
    {
        return await _dbContext.Set().Where(m => ids.Contains(m.Id)).ToListAsync();
    }

    public virtual async Task GetOneAsync(Guid id)
    {
        DbSet dbSet = _dbContext.Set();
        T item = await dbSet.FirstOrDefaultAsync(x => x.Id == id);
        return item;
    }

    public virtual async Task AddAsync(T data)
    {
        _dbContext.Set().Add(data);
        await _dbContext.SaveChangesAsync();
        return data.Id;
    }

    public virtual async Task UpdateAsync(T item)
    {
        DbSet dbSet = _dbContext.Set();
        dbSet.Update(item);
        await _dbContext.SaveChangesAsync();
    }
}

Now, there is a lot I can argue that is wrong with this code such as calling SaveChanges after every command. There are also a lot of things that are questionable such as the use of Guids for all PKs and the existence of this base class in the first place. What I want to focus on instead though is the overall uselessness of this approach. What are we gaining from wrapping up simple LINQ queries in a repository? They aren’t more readable for the most part, unless you aren’t very familiar with LINQ, but that would be a problem in itself. They don’t save code or reduce refactor effort since these are too simplistic to be changed to something different with the same intent. All you’ve accomplished is to abstract away Entity Framework, which isn’t much at all.

Entity Framework is already a generic repository pattern with a UOW pattern built in. I would argue that it has already given you persistence ignorance since you can safely change the underlying database implementation without touching Entity Framework for the most part. If you’ve built the whole system on Entity Framework, then the probability is low that you will abandon an ORM, and almost non-existent that you will choose another ORM. By going with a repository pattern, and especially one with a set up like this, you are introducing complexity where it isn’t warranted. To make matters worse, if you need to compose a larger call that works with more than one repository, you will be working with more than one context. This will prevent you from composing all your commands and then committing them as one transaction.

Reusability Is Still a Principle

That being said, that doesn’t mean that we can write all our queries as one offs. I have seen this done before and the duplication of non-trivial queries became a real problem. If you’re doing something more than just doing a FirstOrDefaultAsync or a ToListAsync, then you should add them to a query class. My personal approach is to store all queries for a given aggregate root in their own class and then inject the queries where necessary. Likewise for commands, although my own experience has seen a far larger reusability issue with queries than commands. You could inject the database context directly into your query class, but I prefer to pass it to the query method itself. This allows me to keep one context for everything in a given request. If you’re just doing simple .Include() queries, then it’s a matter of personal preference if you want to add it to a query class. I tend to use Select statements with hand crafted queries due to their increased performance and control. Since I use Request Injection and I don’t use DTOs in my APIs, I often times utilize the added performance of returning an anonymous type from the query. Regardless of what approach you take, don’t just run around writing non-trivial queries with reckless abandon. You’re still going to want to reuse them in an injected class, but don’t worry about abstracting away entity framework.

Author

Sean Leitzinger

Comments (18)

  1. Anonymous
    April 22, 2019

    Thanks for a great article. Could you please show an example of a “query class”.

    • Sean Leitzinger
      April 22, 2019

      Hi. By query class I just mean a class that contains only queries. So instead of creating a repository that houses both commands and queries you would separate it into two classes. This falls more in line with CQS (Command Query Separation) which, to me, is easier to maintain. You could then take it a step further and do CQRS like what I wrote about here: https://dotnetcultist.com/cqrs-with-entity-framework-core/ In this case I’m using extension methods but you could also do it by injecting the context into a regular class as well like you would with a repository pattern.

  2. Hubert Graham
    May 19, 2019

    A good reason to use to wrap Entity framework in repositories would be if your datastore may change from a supported database type (relational db) to non-supported type (schema-less db i.e mongodb). I personally prefer wrapping away entity framework

  3. Sean Leitzinger
    May 19, 2019

    Hi Hubert, thanks for reading. The probability that your persistence implementation is going to completely change so radically is slim. It’s not by any means a common case, and the vast majority of applications are adding in overhead for something that is 99.9% certain not to happen. Even in the event that you did end up switching to a schema-less database from a relational one, the chance that you can just simply swap out the underlying implementation without any rework to the interface is also slim. I used to wrap entity framework in an abstraction too, but I feel confident saying at this point that it’s just not worth it.

  4. Kelby
    July 15, 2019

    I prefer this pattern because I modularize my repository/data access layer and pattern into a .NET Standard library that many other departments within my company utilize.

    I agree there is overhead, but I like to think it helps me stay organized and in control of all data access.

    • Sean Leitzinger
      July 15, 2019

      If you’re in a distributed environment and you need to control data access then I agree that injecting the context directly is not appropriate. That being said, I would still favor a CQS or CQRS pattern over the repository pattern. Repositories end up just becoming giant dumping grounds.

  5. Jordan
    October 7, 2019

    I have a few comments on this:

    1) 99% of the time when people call their classes “Repositories” they have made a mistake. What they actually have created is a “Dao” (Data Access Object). The Repository pattern is slightly different from Dao and focuses on caching data that has been loaded from the data access layer. I guess the confusion originally arose because frameworks like Hibernate and EF do some caching within their contexts. This doesn’t really relate to anything you have said in your blog post but I just thought I’d mention it, and from this moment on I’ll say “Dao” instead of “Repository” 🙂

    2) I agree with you that calling “SaveChanges()” from inside a Dao is very bad practice. However, this doesn’t mean that Dao is a bad design pattern, it just means that this is an incorrect implementation of it.

    3) The two main benefits of a Dao are (i) Encapsulating data access logic, ie queries and (ii) Hiding your data access implementation, which in this case is Entity Framework.
    You said “My personal approach is to store all queries for a given aggregate root in their own class and then inject the queries where necessary”… so you are still keeping the second benefit, so this is basically a Dao. There is no extra harm in keeping EF implementation details hidden within the data access code of your application.

    Personally I still use Daos, the main benefit being the encapsulation of data access logic. Although I might never switch from EF to NHIbernate or visa versa, to me it seems much cleaner to keep any EF code out of the higher layers of the application. Injecting a DbContext into a Service or Controller seems very bad practice to me.

    • Jordan
      October 7, 2019

      Sorry when I said “so you are still keeping the second benefit” I should have said “so you are still keeping the first benefit”

    • Tim Mc
      October 12, 2020

      If you’re doing “DAO” like this, then you should be doing CQRS. Also, there is nothing stopping you from creating “Extension Methods” for the DbContext to make complex queries more reusable. This way, anytime a DEV imports “System.Data” and starts using your DbContext, viola, all extension methods are imported right along with it “extending” the behavior/functionality of the DbContext. Again, no need for repositories or DAO classes.

  6. Sean Leitzinger
    October 10, 2019

    Hey Jordan, sorry for the late reply. I agree with everything you said about the DAO and what repositories these days usually are in reality. The point I would disagree with you on is that injecting the context directly is a bad practice.

    1. The .NET Core source code uses the context directly in numerous places. The UserStore in ASP.NET Identity for example directly injects the context, and I don’t think we can make the argument that the UserStore is a DAO given everything it does. By your reasoning, this would be considered bad practice. In my view though, this decision makes perfect sense.

    2. The heart of the argument is that I personally feel the DbContext is enough of an abstraction to not warrant wrapping it in a DAO. My current approach is described here where I write extension methods on the context. https://dotnetcultist.com/cqrs-with-entity-framework-core/

    3. I think often times we make our lives far more difficult than they have to be due to always reaching for abstraction and separation. As long as you write your EF code in a reusable manner, I think forcing the context into its own separate layer hidden behind DAOs is just adding in extra work and complexity in most cases.

  7. Jordan
    November 25, 2019

    Hey Sean,

    Sorry about the even later reply!

    Funnily enough I have just reaped the benefit of keeping the DbContext encapsulated in my data access layer, because we have just switched our project from EF Core to NHibernate after finding some bugs and missing features in EF Core (and then realising that EF Core really isn’t a high priority for Microsoft). But I concede that it is rare to switch ORM during the course of the project.

    I think the most important thing is to keep your data acess logic encapsulated somewhere,, so thankyou for the link to your cqrs blog. I’ve definitely got to the point where I’m interested in other approaches to doing things, so I’ll have a proper read of it when I’ve got the time.

    Jordan.

  8. Swetha
    December 23, 2019

    Hello, Thank you very much for this blog post. Is the repository patter good to use if I want to use EF instead of EF core. Also can you please do a blog post on migrations in EF(better way to do if) when using code first approach. Thank you.

  9. Dave Smith
    January 1, 2021

    While I agree switching database stacks is not a likely occurrence, wrapping your data access in something like a repository allows you to compose your app to use multiple datasources. Perhaps some repositories are backed by the database, some by cache, and others by Web API calls. Encapsulating all those within a single unit of work becomes impossible, sure, but we are constantly asked to deliver the impossible.

    • Sean Leitzinger
      January 2, 2021

      The DbContext is already an abstraction on top of ADO.NET and is built as a unit of work. Your DbSets within the context are already repositories. There is nothing stopping you from taking your MongoDB queries and putting them in a wrapper class if you want and then composing processes side by side with Entity Framework. A repository that is properly kept to just persistence ends up being nothing more than some linq queries and commands. Most the time these repositories end up being much more than just persistence, which is a problem in itself. There is absolutely no reason to continue writing these cheap abstractions on top of Entity Framework. It adds nothing and instead opens the door for people to throw stuff into the repository that doesn’t belong there and needlessly increases overhead.

      • Dave Smith
        January 2, 2021

        Hi, Sean. Your response makes total sense to me, but then how do we respond to design guidelines that advise against using framework specific details in business logic?

        • Sean Leitzinger
          January 3, 2021

          I think it depends on what your definition of business logic is. From my perspective, persistence is not part of the business, and data is an output. Persisting an entity is much different from running the business rules and processes associated with an entity. If I inject my DbContext directly into an API controller, then we are currently working at the Application layer. From there it’s about determining how and where you orchestrate the process. For most places, this is done in the controller method. We can orchestrate the following command:

          UpdateAccount(UpdateAccountRequest request)

          Validate the incoming request
          Get the entity from the database
          Map the request to the entity.
          Complete business process/rules associated with the updated entity.
          Persist the updated entity.
          Raise domain event/side effects for updated entity.

          Each one of these is a separate step. Having the Dbcontext in the Application layer is fine, and Entity Framework is a first class citizen in Asp.NET Core. Whether you follow Onion Architecture, Hexagonal Architecture, or some other holistic approach, your domain validation, event definitions, rules, etc. are all going to be completely separate from your persistence and won’t have any need for persistence implementation details. It depends on your set up though. A lot of systems end up using their repository as the point of orchestration or some other deeper service. The ideal set up is to break everything apart and isolate it, then you orchestrate your application code at the top. Everything at that point then is just a step by step play to handle a given process.

  10. joedotnot
    August 9, 2021

    My thoughts exactly, I’ve known not to wrap EF for about 10 years. Reason: KISS (keep it simple, stupid); developers tend to over-engineer sh*t by copying sh*t they seen somewhere without much thought.

Leave a comment

Your email address will not be published. Required fields are marked *