Click here to Skip to main content
13,593,121 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

5.2K views
12 bookmarked
Posted 15 Apr 2018
Licenced CPOL

Implementing and Testing Repository Pattern using Entity Framework

, 19 Apr 2018
Rate this:
Please Sign up or sign in to vote.
There are many blog posts and misconceptions about Repository Pattern, especially since the introduction of the OR/M libraries, like Entity Framework. In this article, we will investigate why this pattern is still useful, what are the benefits of using it… .

There are many blog posts and misconceptions about Repository Pattern, especially since the introduction of the OR/M libraries, like Entity Framework. In this article, we will investigate why this pattern is still useful, what are the benefits of using it and how we can use it in combination with Entity Framework. Even though Microsoft defines its DbContext and DbSet classes as a replacement for this pattern, we will see how this pattern still can help us make our code cleaner.

Let’s start with the definition of Repository Pattern. One of the best definitions of this pattern could be found in Martin Flower’s book Patterns of Enterprise Architecture:

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection.

Why is this definition so cool, one might ask. Well, I personally like it because of the way it emphasizes two very important attributes of Repository pattern. The first attribute is that this pattern is an abstraction that aims to reduce complexity. The second important attribute is that this pattern is, in fact, an in-memory collection that mirrors database tables.

Repository Pattern Benefits and Misconceptions

Even if we use Entity Framework, we might end up with a lot of duplicate query code. For example, if we are implementing blog application and we want to get the most viewed articles in a few places, we might end up with repeated query logic which would look something like this:

var twentyMostViewedPosts = context.Articles
  .Where(a => a.IsPublished)
  .OrderBy(a => a.Views).Take(20);

We could end up with even more complicated queries, that potentially could be repeated through the code. Changing and maintaining this kind of code is not something that could be done in an easy manner. So, we still call our Repository to help us with this. We can encapsulate that behavior inside Repository and just call it like this:

var twentyMostViewedPosts = repository.GetTopTwentyArticles(context);

It is much cleaner this way, isn’t it? Plus, if we want to change something in this logic, we will do it only in one place, which is a huge benefit. There you go, the first benefit of using Repository Pattern – no duplicate query logic.

The second obvious benefit is that it separates application code from persistence framework, and with that from, the database that we are using. Basically, we could use different OR/M in our repository, or use completely different technology, like MongoDB or PostgreSQL, for example. Looking at the changes in the database trends in the last decade, it is quite obvious why we would like to have flexibility like this.

The last, but probably one of the main benefits of using this pattern is that it eases unit testing. However, people often have the misconception that this pattern enables you to test data access layer in an easier manner. That is not true, but it is becoming a great asset in testing business logic. It is easy to mock repository implementation in the business logic.

Repository Pattern Overview

As we already mentioned, Repository is an in-memory collection of objects and that collection needs to have an interface using which we can access elements from that collection. That is why Repository should expose classical CRUD operations. Some people choose to skip Update operation because updating an object from the in-memory collection is essentially getting it and changing its value. I am one of those people 🙂 Of course, if you like it, you can implement this function too.

To sum it up, this is how repository interface and implementation looks like:

As you can see, we are aiming for a generic solution. We will define interface IRepository that is exposing these functions:

  • Add – Method that will add an object of defined type T into our repository
  • Remove – Method that will remove the object of defined type T from our repository
  • Get – Method that will get an object of defined type T from our repository
  • GetAll – Method that will get all objects from our repository
  • Find – Method that will find and retrieve objects that match certain criteria from our repository

Notice that there is nothing like Save method in our Repository, as well. This is where another pattern, called Unit of Work comes into the picture. This is a separate component that holds information about different repositories and implements saving functionality; something like this:

Entity Framework Brief Overview

At this moment, we know that we will use Entity Framework and if you are not familiar with it, you can check MSDN articles about it. In essence, Entity Framework is an object-relational mapper (O/RM) that enables .NET developers to work with a database using .NET objects. It eliminates the need for most of the data-access code that developers usually need to write. In a nutshell, it maps code objects to database tables and vice versa.

There are two approaches when it comes to using EntityFramework. The first one is called database-first approach. In this approach, we create a database and that use Entity Framework to create domain objects and to build code on top of that. The second one is called code-first approach. This is the approach that we will for building our Repository.

There are two important classes that we need to know –  DbContext and DbSetDbContex is one important class for Entity Framework. When using Entity Framework, we need to have one class that derives this class in our system. This way, Entity Framework will know what needs to be created. A class that inherits this DbContext exposes DbSet properties for the types that you want to be part of the model.

Tests, Mocking and Implementation

Cool, now that we know how our Repository should look like, we can start writing our tests for Repository class. One small note before we continue, in this example, I am using xUnit unit test framework and Moq mocking framework. Also, in the spirit of TDD, we will write our tests first and implementation afterwards. Apart from that, testing type that will be used in this example is simple TestClass, and here is how it looks:

public class TestClass
{
    public int Id { get; set; }
}

Very simple, right? It just has one property – IdLet’s define how the interface of our Repository should look so we can understand our implementation better later:

public interface IReporitory<TEntity> where TEntity : class 
{
    void Add(TEntity entity);
    void Remove(TEntity entity);
    TEntity Get(int id);
    IEnumerable<TEntity> GetAll();
    IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
}

Add Method

Ok, let’s now see what the test for Add method looks like:

[Fact]
public void Add_TestClassObjectPassed_ProperMethodCalled()
{
    // Arrange
    var testObject = new TestClass();

    var context = new Mock<DbContext>();
    var dbSetMock = new Mock<DbSet<TestClass>>();
    context.Setup(x => x.Set<TestClass>()).Returns(dbSetMock.Object);
    dbSetMock.Setup(x => x.Add(It.IsAny<TestClass>())).Returns(testObject);

    // Act
    var repository = new Repository<TestClass>(context.Object);
    repository.Add(testObject);

    //Assert
    context.Verify(x => x.Set<TestClass>());
    dbSetMock.Verify(x => x.Add(It.Is<TestClass>(y => y == testObject)));
}

There are quite a few things that require explanation here. Firstly, the assumption is that Repository pattern will rely on DbContext class and that it will receive it through the constructor. That is why in the Arrange section of the test, we mocked this class. We created a mock object of the DbSet class, as well. Then, we set it up in a way that Add method of the DbSet returns testObjectwhich is just an object of the TestClass and Set method of the DbContext returns DbSet mock object.

This is done like this so we can later test these methods being called, which is something you can see in the Assert part of the test. To sum it up, we mock the DbContext and DbSet, then we call the Add method of the repository and finally, we verify those methods from mock objects that have been called.

Implementation of this method is very straight-forward. Here it is:

public class Repository<TEntity> : IReporitory<TEntity> where TEntity : class
{
      protected readonly DbContext Context;
      protected readonly DbSet<TEntity> Entities;

      public Repository(DbContext context)
      {
          Context = context;
          Entities = Context.Set<TEntity>();
      }

      public void Add(TEntity entity)
      {
          Entities.Add(entity);
      }
}

Now you can see why we needed to mock Set method of the DbContextThis method returns a non-generic DbSet instance, using which we can access entities of the given type in the context. Then, we use that instance in the method to add another object.

Remove Method

Implementation of this method is similar to the implementation of the Add method. Let’s see what the test looks like:

[Fact]
public void Remove_TestClassObjectPassed_ProperMethodCalled()
{
    // Arrange
    var testObject = new TestClass();

    var context = new Mock<DbContext>();
    var dbSetMock = new Mock<DbSet<TestClass>>();
    context.Setup(x => x.Set<TestClass>()).Returns(dbSetMock.Object);
    dbSetMock.Setup(x => x.Remove(It.IsAny<TestClass>())).Returns(testObject);

    // Act
    var repository = new Repository<TestClass>(context.Object);
    repository.Remove(testObject);

    //Assert
    context.Verify(x => x.Set<TestClass>());
    dbSetMock.Verify(x => x.Remove(It.Is<TestClass>(y => y == testObject)));
}

Almost the same as for the Add method, we just need to setup Remove method of the DbSet. Here is how the implementation of the Repository class looks once we add Remove method:

public class Repository<TEntity> : IReporitory<TEntity> where TEntity : class
{
    protected readonly DbContext Context;
    protected readonly DbSet<TEntity> Entities;

    public Repository(DbContext context)
    {
        Context = context;
        Entities = Context.Set<TEntity>();
    }

    public void Add(TEntity entity)
    {
        Entities.Add(entity);
    }

    public void Remove(TEntity entity)
    {
        Entities.Remove(entity);
    }
}

Now, since during the implementation of the Add method, we properly initialized DbContext and DbSe<wbr />t, it is easy for us to add other methods.

Get Method

During the implementation of the Get method, we are following the same principles. Test for this method looks like this:

[Fact]
public void Get_TestClassObjectPassed_ProperMethodCalled()
{
    // Arrange
    var testObject = new TestClass();

    var context = new Mock<DbContext>();
    var dbSetMock = new Mock<DbSet<TestClass>>();

    context.Setup(x => x.Set<TestClass>()).Returns(dbSetMock.Object);
    dbSetMock.Setup(x => x.Find(It.IsAny<int>())).Returns(testObject);

    // Act
    var repository = new Repository<TestClass>(context.Object);
    repository.Get(1);

    // Assert
    context.Verify(x => x.Set<TestClass>());
    dbSetMock.Verify(x => x.Find(It.IsAny<int>()));
}

Repository class after the addition of the Get method looks like this:

public class Repository<TEntity> : IReporitory<TEntity> where TEntity : class
{
    protected readonly DbContext Context;
    protected readonly DbSet<TEntity> Entities;

    public Repository(DbContext context)
    {
        Context = context;
        Entities = Context.Set<TEntity>();
    }

    public void Add(TEntity entity)
    {
        Entities.Add(entity);
    }

    public void Remove(TEntity entity)
    {
        Entities.Remove(entity);
    }

    public TEntity Get(int id)
    {
        return Entities.Find(id);
    }

GetAll Method

For the GetAll method, we need to shake things up a little bit. This method needs to return the list of objects. This means that we need to create a list of TestClass objects and return it through DbSetEffectively, this means that we need to mock part of the DbSet that implements the IQueryableinterface in tests. Here is how it is done:

[Fact]
public void GetAll_TestClassObjectPassed_ProperMethodCalled()
{
    // Arrange
    var testObject = new TestClass() { Id = 1 };
    var testList = new List<TestClass>() { testObject };

    var dbSetMock = new Mock<DbSet<TestClass>>();
    dbSetMock.As<IQueryable<TestClass>>().Setup(x => x.Provider).Returns
                                         (testList.AsQueryable().Provider);
    dbSetMock.As<IQueryable<TestClass>>().Setup(x => x.Expression).
                                         Returns(testList.AsQueryable().Expression);
    dbSetMock.As<IQueryable<TestClass>>().Setup(x => x.ElementType).Returns
                                         (testList.AsQueryable().ElementType);
    dbSetMock.As<IQueryable<TestClass>>().Setup(x => x.GetEnumerator()).Returns
                                         (testList.AsQueryable().GetEnumerator());

    var context = new Mock<DbContext>();
    context.Setup(x => x.Set<TestClass>()).Returns(dbSetMock.Object);

    // Act
    var repository = new Repository<TestClass>(context.Object);
    var result = repository.GetAll();

    // Assert
    Assert.Equal(testList, result.ToList());
}

We needed to mock Provider, Expression, ElementType and GetEnumerator(<wbr />) with the properties from created test list. As it turns out, writing a test for this method is a bigger challenge than writing the implementation itself. Here is our Repository class extended with the GetAll method:

public class Repository<TEntity> : IReporitory<TEntity> where TEntity : class
{
  protected readonly DbContext Context;
  protected readonly DbSet<TEntity> Entities;

  public Repository(DbContext context)
  {
      Context = context;
      Entities = Context.Set<TEntity>();
  }

  public void Add(TEntity entity)
  {
      Entities.Add(entity);
  }

  public void Remove(TEntity entity)
  {
      Entities.Remove(entity);
  }

  public TEntity Get(int id)
  {
      return Entities.Find(id);
  }

  public IEnumerable<TEntity> GetAll()
  {
      return Entities.ToList();
  }
}

Find Method

After learning how to mock IQueryable in the previous example, writing tests for Find method is much easier. We follow the same principle we used for the GetAll method. The test looks like this:

[Fact]
public void Find_TestClassObjectPassed_ProperMethodCalled()
{
    var testObject = new TestClass(){Id = 1};
    var testList = new List<TestClass>() {testObject};

    var dbSetMock = new Mock<DbSet<TestClass>>();
    dbSetMock.As<IQueryable<TestClass>>().Setup(x => x.Provider).Returns
                                               (testList.AsQueryable().Provider);
    dbSetMock.As<IQueryable<TestClass>>().Setup(x => x.Expression).Returns
                                               (testList.AsQueryable().Expression);
    dbSetMock.As<IQueryable<TestClass>>().Setup(x => x.ElementType).Returns
                                               (testList.AsQueryable().ElementType);
    dbSetMock.As<IQueryable<TestClass>>().Setup(x => x.GetEnumerator()).Returns
                                               (testList.AsQueryable().GetEnumerator());

    var context = new Mock<DbContext>();
    context.Setup(x => x.Set<TestClass>()).Returns(dbSetMock.Object);

    var repository = new Repository<TestClass>(context.Object);

    var result = repository.Find(x => x.Id == 1);

    Assert.Equal(testList, result.ToList());
}

And finally, the complete implementation of the Repository class looks like this:

public class Repository<TEntity> : IReporitory<TEntity> where TEntity : class
{
    protected readonly DbContext Context;
    protected readonly DbSet<TEntity> Entities;

    public Repository(DbContext context)
    {
        Context = context;
        Entities = Context.Set<TEntity>();
    }

    public void Add(TEntity entity)
    {
        Entities.Add(entity);
    }

    public void Remove(TEntity entity)
    {
        Entities.Remove(entity);
    }

    public TEntity Get(int id)
    {
        return Entities.Find(id);
    }

    public IEnumerable<TEntity> GetAll()
    {
        return Entities.ToList();
    }

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

Conclusion

A lot of people argue that if we are going to use DbContext and DbSet, we don’t need to implement Repository Pattern and Unit of Work. If you ask me if this is the case, I would say to you that it depends on the type of problem. In this article, you had the chance to see how to build and test generic Repository using Entity Framework. If you choose to use DbContext and DbSet, there are still some useful tips that you could use from this article, like how to mock these classes. 

Thanks for reading!


Read more posts from the author at Rubik’s Code.


 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Nikola M. Živković
Software Developer (Senior) Vega IT Sourcing
Serbia Serbia
Read more at my blog: https://rubikscode.net

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionNice Article but Pin
Member 1265448517-Apr-18 2:59
professionalMember 1265448517-Apr-18 2:59 
QuestionSorry I disagree Pin
Paul Tait16-Apr-18 18:34
memberPaul Tait16-Apr-18 18:34 
AnswerRe: Sorry I disagree Pin
mazaaz16-Apr-18 19:13
membermazaaz16-Apr-18 19:13 
GeneralRe: Sorry I disagree Pin
Paul Tait16-Apr-18 19:34
memberPaul Tait16-Apr-18 19:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web04-2016 | 2.8.180618.1 | Last Updated 20 Apr 2018
Article Copyright 2018 by Nikola M. Živković
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid