According to Martin Fowler:

Benefits:

Helps minimize duplicate query logic.

Imagine for a moment that in your application you have to retrieve Stock Portfolios with Securities based on a condition.

if(!includeProps) {
            return await context.Porfolios.FindAsync(id);
        }
      return await context.Porfolios
        .Include(p => p.Securities)
        .ThenInclude(ps => ps.Security)
        .SingleOrDefaultAsync(p => p.Id == id);

Without the repository pattern we ended up duplicating this query logic in many places in our application.

In situations like this a good approach would be to encapsulate this logic into a method in our Portfolio Repository and then just call a method like this to retrieve a portfolio.

var porfolio = await repository.GetPorfolio(id, includeProps: false);

This method which takes 2 parameters, the portfolio id and the option to eager load securities.

Another benefit of the repository pattern is that it decouples your application from persistence frameworks like Entity Framework, NHibernate, MicroORMs(Dapper) etc. This is really helpful in scenarios when is needed to switch to a different persistence framework since this reduce the impact of dependencies in the rest of the application.

A very common repository interface will look like this, basically it represents a simple collection of items in memory

Add(entity)

Remove(entity)

Get(id)

GetAll()

Find(predicate)

Notice that there is not Update or Save method in here since the repository only place the role of a collection of items in memory, if we had an update or save method it would be as silly as having a Save and Update methods in an Array.

You might be asking now, well how or when are we going to save our changes to the Database.

In order to answer this, I’ll introduce another design pattern, The Unit of Work Pattern.

According to Martin Fowler:

A Unit of Work will coordinate persistence changes across multiple repositories in one transaction.

I’ll show a simple implementation of the unit of work and repository pattern.

First we need a repository interface.

It should look something like this

Add()

Remove()

Get(id)

Find(predicate)

Let’s call it IRepository, this interface is a like the interface for a collection of of objects in memory as we described before, then of course we need to implement this interface in a Repository Class.

public interface IRepository<T> where T : class
{
   T Get(int id);
   IEnumerable<T> GetAll();
   IEnumerable<T> Find(Expression<Func<T, bool>> predicate);
   void Add(T entity);
   void Remove(T entity);
}
public class Repository<T> : IRepository<T> where T : class {
  private readonly DbSet<T> _entities;

  public Repository (DbContext context) {
    _entities = context.Set<T> ();
  }

  public T Get (int id) {
    return _entities.Find (id);
  }

  public IEnumerable<T> GetAll () {
    return _entities.ToList ();
  }

  public IEnumerable<T> Find (Expression<Func<T, bool>> predicate) {
    return _entities.Where (predicate);
  }

  public void Add (T entity) {
    _entities.Add (entity);
  }

  public void Remove (T entity) {
    _entities.Remove (entity);
  }
  public T SingleOrDefault (Expression<Func<T, bool>> predicate) {
    return _entities.SingleOrDefault (predicate);
  }
}

Inside this repository we will have a DBContext.

This repository is completely generic, there isn’t anything here related to our application

public class InvestipsContext : DbContext {
  public InvestipsContext () : base ("name=InvestipsContext") {
    this.Configuration.LazyLoadingEnabled = false;
  }

  public virtual DbSet<Porfolio> Porfolios { get; set; }
  public virtual DbSet<Security> Securities { get; set; }
  public virtual DbSet<Exchange> Exchanges { get; set; }

  protected override void OnModelCreating (DbModelBuilder modelBuilder) {
    modelBuilder.Configurations.Add (new PorfolioConfiguration ());
  }
}

Then for each entity in our DBContext we will have a repository so for example for Portfolio Entity I will have an IPorfolioRepository interface and a PorfolioRepository concrete implementation of  IPorfolioRepository interface.

In the IPorfolioRepository interface we need to define any operations specific to portfolios that are not in our generic repository

Task<Porfolio> GetPorfolio(int id, bool includeProps = true);
Task<List<Porfolio>> GetPorfoliosWithTopSecurities();
public class PorfolioRepository : Repository<Porfolio>, IPorfolioRepository {
  private readonly InvestipsContext _context;
  public PorfolioRepository (InvestipsContext context) {
    _context = context;
  }

  public IEnumerable<Porfolio> GetTopSellingCourses (int count) {
    return _context.Porfolios.OrderByDescending (c => c.Name).ToList ();
  }

  public IEnumerable<Course> GetPorfoliosWithSecurities () {
    return _context.Porfolios
      .Include (p => p.Securities)
      .OrderBy (s => s.Symbol)
      .ToList ();
  }
}

In GetPorfoliosWithSecurities method we are using eager loading to get Securities into portfolios. As you probably know, queries can become very complex and having the ability to reuse them is very important. This allow us to have clean code and make your applications easier to maintain.

For the Unit of work, we also are going to need an Interface IUnitOfWork with its respective concrete implementation UnitOfWork class,

The porpoise of this interface is to define the contracts of the repositories we want to expose based on the entities we have in our DBContext,  for example

public interface IUnitOfWork : IDisposable {
  IPorfolioRepository Porfolios { get; }
  ISecurityRepository Securities { get; }
  int Save ();
}

Notice that we are exposing Interfaces for repositories and IUnitOfWork so we can provide mock ups in our unit tests.

public class UnitOfWork : IUnitOfWork {
  private readonly InvestipsContext _context;

  public UnitOfWork (InvestipsContext context) {
    _context = context;
    Porfolios = new CourseRepository (_context);
    Securities = new SecurityrRepository (_context);
  }

  public IPorfolioRepository Porfolios { get; private set; }
  public ISecurityRepository Securities { get; private set; }

  public int Save () {
    return _context.SaveChanges ();
  }

  public void Dispose () {
    _context.Dispose ();
  }
}

A key point her is that we use the same context to initialize all our repositories in that way the client of Unit of work will instantiate a InvestipContext and then use the same context in all repositories

Save here is use to basically persist changes and complete the unit of work.

By now you probably will be asking. Ok now that I have a unit of work how do I Use it in my own applications?

Here are some examples how I use the repository pattern and unit of work in some of my applications.

Console Application Example:

class Program {
  static void Main (string[] args) {
    using (var unitOfWork = new UnitOfWork (new InvestipsContext ())) {
      // Test 1
      var course = unitOfWork.Porfolios.Get ();

      // Test 2
      var courses = unitOfWork.Securities.GetTopSecurities (25);

      // Test 3
      var author = unitOfWork.Porfolios.Get (34);
      unitOfWork.Porfolios.Remove (author);
      unitOfWork.Save ();
    }
  }
}

In Asp.net Core with dependency Injection:

public class PorfoliosController : Controller
{
    private readonly IMapper mapper;
    private readonly IUnitOfWork uow;

    public PorfoliosController(IMapper mapper, IUnitOfWork uow)
    {
      this.uow = uow;
      this.mapper = mapper;
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetPorfolio(int id)
    {
      var porfolio = await uow.Profolios.Get(id);

      if (porfolio == null)
      {
        return NotFound();
      }

      var porfolioResource = mapper.Map<Porfolio, PorfolioResource>(porfolio);
      return Ok(porfolioResource);
    }
}