Skip to content

Repository Pattern in .Net Core

A couple of years ago, I wrote about the Repository and Unit of Work pattern. Although this post is quite old and not even .net core, I get many questions about it. Since the writing of the post, .Net core matured and I learned a lot about software development. Therefore, I wouldn’t implement the code as I did back then. Today, I will write about implementing .the repository pattern in .Net core

Why I am changing the Repository Pattern in .Net Core

Entity Framework Core already serves as unit of work. Therefore you don’t have to implement it yourself. This makes your code a lot simpler and easier to understand.

The Repository Pattern in .Net Core

For the demo, I am creating a simple 3-tier application consisting of controller, services, and repositories. The repositories will be injected into the services using the built-in dependency injection. You can find the code for the demo on Github.

In the data project, I have my models and repositories. I create a generic repository that takes a class and offers methods like get, add, or update.

Implementing the Repositories

 public class Repository<TEntity> : IRepository<TEntity> where TEntity : class, new()
    {
        private readonly RepositoryPatternDemoContext _repositoryPatternDemoContextContext;

        public Repository(RepositoryPatternDemoContext repositoryPatternDemoContextContext)
        {
            _repositoryPatternDemoContextContext = repositoryPatternDemoContextContext;
        }

        public IQueryable<TEntity> GetAll()
        {
            try
            {
                return _repositoryPatternDemoContextContext.Set<TEntity>();
            }
            catch (Exception)
            {
                throw new Exception("Couldn't retrieve entities");
            }
        }

        public async Task<TEntity> AddAsync(TEntity entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException($"{nameof(AddAsync)} entity must not be null");
            }

            try
            {
                await _repositoryPatternDemoContextContext.AddAsync(entity);
                await _repositoryPatternDemoContextContext.SaveChangesAsync();

                return entity;
            }
            catch (Exception)
            {
                throw new Exception($"{nameof(entity)} could not be saved");
            }
        }

        public async Task<TEntity> UpdateAsync(TEntity entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException($"{nameof(AddAsync)} entity must not be null");
            }

            try
            {
                _repositoryPatternDemoContextContext.Update(entity);
                await _repositoryPatternDemoContextContext.SaveChangesAsync();

                return entity;
            }
            catch (Exception)
            {
                throw new Exception($"{nameof(entity)} could not be updated");
            }
        }
    }

This repository can be used for most entities. In case one of your models needs more functionality, you can create a concrete repository that inherits from Repository. I created a ProductRepository which offers a Product-specific method:

public class ProductRepository : Repository<Product>, IProductRepository
    {
        private readonly RepositoryPatternDemoContext _repositoryPatternDemoContextContext;

        public ProductRepository(RepositoryPatternDemoContext repositoryPatternDemoContextContext) : base(repositoryPatternDemoContextContext)
        {
            _repositoryPatternDemoContextContext = repositoryPatternDemoContextContext;
        }

        public Product MyProductSpecificMethod()
        {
            // Get some amazing data from the database and then return it;
            // return _repositoryPatternDemoContextContext.GetAmazingData();

            return new Product
            {
                Name = "ProductName",
                Description = "Special Description",
                Price = 1234567m
            };
        }
    }

The ProductRepository also offers all generic methods because its interface IProductRepository inherits from IRepository:

 public interface IProductRepository : IRepository<Product>
    {
        Product MyProductSpecificMethod();
    }

The last step is to register the generic repositories and the concrete repository in the Startup class.

services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
services.AddTransient<IProductRepository, ProductRepository>();

The first line registers the generic attributes. This means if you want to use it in the future with a new model, you don’t have to register anything else. The second line registers the concrete implementation of the ProductRepository.

Implementing Services which use the Repositories

I implement two services, the CustomerService and the ProductService. Each service gets injected a repository. The ProductServices uses the IProductRepository and the CustomerService uses the IRepository<Customer>. Inside the services, you can implement whatever business logic your application needs. I implemented only simple calls to the repository but you could have complex calculations and several repository calls in a single method.

public class CustomerService : ICustomerService
    {
        private readonly IRepository<Customer> _customerRepository;

        public CustomerService(IRepository<Customer> customerRepository)
        {
            _customerRepository = customerRepository;
        }

        public List<Customer> GetAllCustomer()
        {
            return _customerRepository.GetAll().ToList();
        }

        public async Task<Customer> GetCustomerById(int id)
        {
            return await _customerRepository.GetAll().FirstOrDefaultAsync(x => x.Id == id);
        }

        public async Task<Customer> AddCustomer(Customer newCustomer)
        {
            return await _customerRepository.AddAsync(newCustomer);
        }
    }
 public class ProductService : IProductService
    {
        private readonly IProductRepository _productRepository;

        public ProductService(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }

        public Product GetMySpecialProduct()
        {
            return _productRepository.MyProductSpecificMethod();
        }

        public async Task<Product> GetProductById(int id)
        {
            return await _productRepository.GetAll().FirstOrDefaultAsync(x => x.Id == id);
        }
    }

To get data, I am using GetAll for all my get methods. I can do this because GetAll returns an IQueryable object. This means that it contains only the call to the database but it wasn’t executed yet. In my GetProductById I use FirstOrDefault to get the first product with the id I want. This executes the database call and is fast because it only returns one (or no) product object.

return await _productRepository.GetAll().FirstOrDefaultAsync(x => x.Id == id);

Implementing the Controller to test the Application

To test the application, I implemented a really simple controller. The controllers offer for each service method a parameter-less get method and return whatever the service returned. Each controller gets the respective service injected.

 public class CustomerController
    {
        private readonly ICustomerService _customerService;

        public CustomerController(ICustomerService customerService)
        {
            _customerService = customerService;
        }

        public async Task<ActionResult<Customer>> CreateCustomer()
        {
            var customer = new Customer
            {
                Age = 30,
                FirstName = "Wolfgang",
                LastName = "Ofner"
            };

            return await _customerService.AddCustomer(customer);
        }

        public ActionResult<List<Customer>> GetAllCustomers()
        {
            return _customerService.GetAllCustomer();
        }

        public async Task<ActionResult<Customer>> GetCustomerById()
        {
            return await _customerService.GetCustomerById(1);
        }
    }
 public class ProductController
    {
        private readonly IProductService _productService;

        public ProductController(IProductService productService)
        {
            _productService = productService;
        }

        public async Task<ActionResult<Product>> GetProductById()
        {
            return await _productService.GetProductById(1);
        }

        public ActionResult<Product> GetSpecialProduct()
        {
            return _productService.GetMySpecialProduct();
        }
    }

When you call the create customer action, a customer object in JSON should be returned.

Test the creation of a customer Repository Pattern in .Net Core
Test the creation of a customer

Use the database

If you want to use the a database, you have to add your connection string in the appsettings.json file. My connection string looks like this:

 
"ConnectionString": "Server=localhost;Database=RepositoryPatternDemo;Integrated Security=False;Persist Security Info=False;User ID=sa;Password=<YourNewStrong@Passw0rd>"

I also added an SQL script to create the database, tables and test data. You can find the script here.

Conclusion

In today’s post, I gave my updated opinion on the repository pattern and simplified the solution compared to my post a couple of years ago. This solution uses entity framework core as unit of work and implements a generic repository that can be used for most of the operations. I also showed how to implement a specific repository, in case the generic repository can’t full fill your requirements. Implement your own unit of work object only if you need to control over your objects.

You can find the code for the demo on Github.

Published inDesign PatternProgramming

16 Comments

  1. Mahesh HK Mahesh HK

    Nice Article!! But I have questions on the implementations. By adding new entity to the context and saving it immediately on the store, we are not achieving the goals unit of work.

    await _repositoryPatternDemoContextContext.AddAsync(entity);
    await _repositoryPatternDemoContextContext.SaveChangesAsync();

    By doing so, I feel we cannot handle multiple transactions to the context and then update the data store once.

    If you can throw light on how to handle multiple transactions on context and then update all at once to data store that would be great.

    • Hi Mahesh,

      you could use AddRangeAsync and pass a list of entities or you could remove the SaveChangesAsync() call and create a second method, for example Complete() or Save() and put SaveChangesAsync() into this new method. Then you can add whatever you want to the dbcontext and save it when you call the Complete() method.

      • Richie Richie

        Hi Wolfgang, in follow up to Mahesh’s question and your response, what if we had services that had work to do over multiple repositories and should anything go wrong during this, all work in all repositories should not be saved? In your reply above are you saying that we should have the “complete” method in all repositories, let’s say via the generic, and as long as this gets called at the end of a successful journey by the service via any of the repositories, all savechanges() are applied across all repositories?

        I guess the example used in the article equates to very small, direct units of work so I’m finding it a little confusing on illustrating the purpose of a unit of work pattern in this instance.

        • Hi Richie,
          you could have a method which does all the work and saves to the database at the end. for example:

          public void MightyMethod(Customer customer, Order order, Payment payment)
          {
              try
              {
                  _customerService.AddWithoutSaving(customer);
                  _orderService.AddWithoutSaving(order);
                  _paymentService.AddWithoutSaving(payment);
          
                  _dbContext.SaveChanges();
              }
              catch (Exception ex)
              {
                  logger.LogError(ex.Message);
              }
          }
          

          In this example, three services add their objects but don’t save to the database. The services could also do validation or whatever you need. If everything was ok, SaveChanges is executed. If something went wrong, the exception message will logged but nothing will be saved to the database.

          Also look into transactions, so you can rollback all the changes if something went wrong. Here is the documentation of Microsoft: https://docs.microsoft.com/en-us/ef/core/saving/transactions

  2. AndrzejM AndrzejM

    I would like each service to have it’s own DbContext and coordinate saving in the transaction.
    But currently .NET Core does not allow this (see https://docs.microsoft.com/en-us/ef/core/saving/transactions#limitations-of-systemtransactions).

    “`
    public void MightyMethod(Customer customer, Order order, Payment payment)
    {
    using (var ts = new TransactionScope(…))
    {
    try
    {
    _customerService.AddAndSave(customer);
    _orderService.AddAndSave(order);
    _paymentService.AddAndSave(payment);

    ts.Complete();
    }
    catch (Exception ex)
    {
    logger.LogError(ex.Message);
    }
    }
    }
    “`

    • Hi AndrzejM

      Why do you want each service to have a separate DbContext? Are you saving the data in different databases? If not, I don’t see a reason why you would need different DbContexts.

      You could look into microservices to have each service independent from each. This would be a major change though. Check out my article about microservices and the following ones for my implementation of one: https://www.programmingwithwolfgang.com/microservices-getting-started

  3. Mark Mark

    I have just started a project and used the exact same pattern initially.

    There is a lot of debate about the value of Repository Pattern with EF as it is itself an abstraction. In the example above MightyMethod i see _context is being accessed directly in a service – not via a repository which in some ways is the entire point. This also means in addition to mocking repositories, you’d need to mock a _context or use in-memory database. But if you’re doing that (use in-memory db for testing), again what is the point of using a repository pattern if you’re not going to mock them for unit testing?

    Curious how others are handling this. Love repository pattern when not using EF, but when using it, the abstraction seems overkill and you end up referencing EF directly in services to get its full value anyway.

    • Hi Mark,
      I totally agree with you that there is no right answer whether to use the Repository Pattern or not and there are many different opinions on that topic. My opinion is that you shouldn’t use it if you don’t need it. Keep it as simple as possible.
      I can’t follow what you mean with the service accessing the database context. All my services call a repository method and only knows the repository about the database context. This structure can be easily mocked for unit tests and if you want to switch between in-memory and a real database, you only have to change the following line in the Startup.cs: services.AddDbContext(options => options.UseSqlServer(Configuration[“Database:ConnectionString”])); instead of UseSqlServer use UseInMemoryDatabase

  4. Nicolas Nicolas

    Isn’t it a bit dangerous to register a Repository as transient?

    • Hi Nicolas,

      no it’s no problem to use transient. The only exception would be if you want to use transactions over multiple repositories. Then you must take Scoped. The db context of EF core uses Scoped by default.

  5. Alex Alex

    Thank you very much for your article.
    I have two queries.
    1. I understand that the logic must be assembled in the services, if within a service I need to use the methods of other repositories, should I instantiate them within the constructor of the service? Could you give me an example that is not clear to me.
    2. In the Startup class should I instantiate all my repositories?

    • Hi Alex,

      1. you instantiate your second repository in the constructor of the service using dependency injection. The could would look something like this:

      public class ProductService : IProductService
         {
             private readonly IProductRepository _productRepository;
             private readonly IOrderRepository _orderRepository;
       
             public ProductService(IProductRepository productRepository, IOrderRepository orderRepository)
             {
                 _productRepository = orderRepository;
                 _orderRepository= orderRepository;
             }
      

      Then you can use the _orderRepository to call all methods needed from this repository.

      2. You don’t instantiate the repositories in the Startup class but you register them with the DI container. This means you tell the DI container that everytime it sees an IRepository, it should inject an instance of Repository. If you use a generic repository, for example, IRepository. then you don’t have to do anything. If you use a concrete repository like in the example above (IOrderRepository) then you have to register it in the Startup class. In this case you have to add the following line:

      services.AddTransient<IOrderRepository , OrderRepository >();
      

      • Alex Alex

        Thanks for the explanation, another doubt.
        Now that the unit of work does not exist, the Savechanges are given within the repository directly. It must be so? I say this because I can implement the dispose method, or you change the validations before giving savechanges. Since if it ever changes, it would have to do it in the repository in each method.

        • Yes the SaveChanges has to be in the repository because it is executed on the context. The validation should be splitted in two parts. First when you receive data, e.g. from a REST call. There, I would use FluentValidator to validate that the data I receive is valid. The second validation should be in the repository because only so can you guarantee that you save correct data. I have if (entity == null) in the repository which is the simplest form of validation.

  6. hamza hamza

    _repositoryPatternDemoContextContext.Set();

    set() i am unable to found

Leave a Reply

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

RSS
Follow by Email
LinkedIn
Share