Repository and Unit of Work Pattern .Net Core

The Repository and Unit of Work pattern creates an abstraction layer between Data Access Layer (DAL) and the business logic. This isolates concerns making it easier to test or implement TDD.

The Repository Pattern

What do we need

  1. A repository interface for the entity
  2. The implementation of such interface
  3. Registration of the repository service in the application
  4. Inject and use it in the controllers

Example

1. Create an interface for the entity.
using System.Collections.Generic;

namespace MyApi.Models
{
    public interface IBlogPostRepository
    {
        void Add(BlogPost post);
        IEnumerable GetAll();
        BlogPost Find(int key);
        void Remove(int key);
        void Update(BlogPost item);
    }
}
2. Implement the interface
using System;
using System.Collections.Generic;
using System.Linq;

namespace MyApi.Models
{
    public class BlogPostRepository : IBlogPostRepository
    {
        private readonly BlogPostContext _context;

        public BlogPostRepository(BlogPostContext context)
        {
            _context = context;
            Add(new BlogPost { Title = "Hello World!" });
        }

        public IEnumerable GetAll()
        {
            return _context.BlogPosts.ToList();
        }

        public void Add(BlogPost post)
        {
            _context.BlogPosts.Add(post);
            _context.SaveChanges();
        }

        public BlogPost Find(int key)
        {
            return _context.BlogPosts.FirstOrDefault(p => p.Key == key);
        }

        public void Remove(int key)
        {
            var entity = _context.BlogPosts.First(p => p.Key == key);
            _context.BlogPosts.Remove(entity);
            _context.SaveChanges();
        }

        public void Update(BlogPost post)
        {
            _context.BlogPosts.Update(post);
            _context.SaveChanges();
        }
    }
}
3. Register the service

This is usually done in Startup.cs.
Read about Dependency Injection and how to register services in the docs.

services.AddScoped<IBlogPostRepository, BlogPostRepository>();
4. Inject it to the controller via the constructor.
namespace MyApi.Controllers
{
    [Route("api/[controller]")]
    public class BlogPostController : Controller
    {
        public IBlogPostRepository BlogPosts { get; set; }
        public BlogPostController(IBlogPostRepository blogPostItems)
        {
            BlogPosts = blogPostItems;
        }
    }
}

Unit Of work

The docs explain it clearly:

The unit of work class serves one purpose: to make sure that when you use multiple repositories, they share a single database context. That way, when a unit of work is complete you can call the SaveChanges method on that instance of the context and be assured that all related changes will be coordinated. All that the class needs is a Save method and a property for each repository. Each repository property returns a repository instance that has been instantiated using the same database context instance as the other repository instances.

What do we need

  1. Unit of Work Interface
  2. Unit of Work Implementation Class
  3. Register as a service
  4. Inject it and use it in the Controllers

Example

1. Interface
public interface IUnitOfWork
{
    IBlogPostRepository BlogPostRepository { get; }
    IPageRepository PageRepository { get; }
    void Save();
}
2. Interface
public class UnitOfWork : IUnitOfWork
{
    private readonly BlogPostContext _context;
    {
    IBlogPostRepository _blogPostRepository { get; }
    IPageRepository _pageRepository { get; }
    public UnitOfWork(BlogPostContext context)
    {
        _context = context;
    }

    public IBlogPostRepository BlogRepository
    {
        get
        {
            return _blogPostRepository = _blogPostRepository ?? new IBlogPostRepository(_context);
        }
    }

    public IPageRepository IPageRepository
    {
        get
        {
            return _pageRepository = _pageRepository ?? new IPageRepository(_context);
        }
    }

    public void Save()
    {
        _context.SaveChanges();
    }
}
3. Register as a service for DI
services.AddScoped<IUnitOfWork, UnitOfWork>();
4.Use in the controllers
_unitOfWork.BlogPostRepository.Create(new BlogPost()
{
    Title = "My new title"
});

_unitOfWork.PageRepository.Create(new Page()
{
    Title = "Sample Title",
    Content = "Sample content"
});

_unitOfWork.Save();