Implementing the repository pattern

This recipe is an implementation of the repository pattern which allows us to separate the usage of a database and its data from the act of reading that data.

Getting ready

We will be using NuGet Package Manager to install the Entity Framework 4.1 assemblies.

The package installer can be found at http://www.nuget.org/.

We will also be using a database for connecting to and updating data.

Open the Repository Pattern solution in the included source code examples.

Execute the database setup script from the code samples included with this recipe. This can be found in the DataAccess project within the Database folder.

How to do it...

  1. In the DataAccess project, add a new C# interface name IBlogRepository with the following code:
    using System.Linq;
    namespace DataAccess
    {
      public interface IBlogRepository
      {
        IQueryable<T> Set<T>() where T : class;
      }
    }
  2. In the DataAccess project, create a new C# class named BlogRepository with the following code:
    using System.Data.Entity;
    using System.Linq;
    using BusinessLogic;
    namespace DataAccess
    {
      public class BlogRepository : IBlogRepository
      {
        private readonly IDbContext _context;
        public BlogRepository(IDbContext context)
        {
          _context = context;
        }
        public IQueryable<T> Set<T>() where T : class
        {
          return _context.Find<T>();
        }
      }
    }
  3. First, we start by adding a new unit test in the Test project that defines a test for using repository with the following code:
    using BusinessLogic;
    using DataAccess;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Rhino.Mocks;
    namespace Test
    {
      [TestClass]
      public class RepositoryTest
      {
        [TestMethod]
        public void ShouldAllowGettingASetOfObjectsGenerically()
        {
          //Arrange
          IDbContext mockContext = MockRepository.GenerateMock<IDbContext>();
          IBlogRepository repository = new BlogRepository(mockContext);
          //Act
          var items = repository.Set<Blog>();
          //Assert
          mockContext.AssertWasCalled(x => x.Find<Blog>());
        }
      }
    }
  4. In the BusinessLogic project, add a new C# interface name IBlogRepository with the following code:
    using System.Linq;
    namespace DataAccess
    {
      public interface IBlogRepository
      {
        IQueryable<T> Set<T>() where T : class;
      }
    }
  5. In the DataAccess project, create a new C# class named BlogRepository with the following code:
    using System.Data.Entity;
    using System.Linq;
    using BusinessLogic;
    namespace DataAccess
    {
      public class BlogRepository : IBlogRepository
      {
        private readonly IDbContext _context;
        public BlogRepository(IDbContext context)
        {
          _context = context;
        }
        public IQueryable<T> Set<T>() where T : class
        {
          return _context.Find<T>();
        }
      }
    }
  6. In the BlogController update the usage of BlogContext to use IBlogRepository with the following code:
    using System.Linq;
    using System.Web.Mvc;
    using BusinessLogic;
    using DataAccess;
    using UI.Properties;
    namespace UI.Controllers
    {
      public class BlogController : Controller
      {
        private IBlogRepository _blogRepository;
        public BlogController() : this(new BlogRepository(new BlogContext(Settings.Default.BlogConnection))) { }
        public BlogController(IBlogRepository blogRepository)
        {
          _blogRepository = blogRepository;
        }
        //
        // GET: /Blog/
        public ActionResult Display()
        {
          Blog blog = _blogRepository.Set<Blog>().First();
          return View(blog);
        }
      }
    }

How it works...

We start off with a test that defines what we hope to accomplish. We use mocking (or verifiable fake objects) to ensure that we get the behavior that we expect. The test states that any BlogRepository will communicate with the context to connect for the data. This is what we are hoping to accomplish as doing so allows us to layer tests and extension points into the domain.

The usage of the repository interface is a key part of this flexible implementation as it will allow us to leverage mocks, and test the business layer, while still maintaining an extensible solution. The interface to the context is a straightforward API for all database communication. In this example, we only need to read data from the database, so the interface is very simple.

Even in this simple implementation of the interface, we see that there are opportunities to increase reusabilty. We could have created a method or property that returned the list of blogs, but then we would have had to modify the context and interface for every new entity. Instead, we set up the Find method to take a generic type, which allows us to add entities to the usage of the interface without modifying the interface. We will only need to modify the implementation.

Notice that we constrained the interface to accept only the reference types for T, using the where T : class constraint. We did this because value types cannot be stored using entity framework. If you had a base class, you could use it here to constrain the usage of the generic even further. Importantly, not all reference types are valid for T, but the constraint is as close as we can get using C#. Interfaces are not valid because Entity Framework cannot construct them when it needs to create an entity. Instead, it will produce a runtime exception as they are valid reference types.

Once we have the context, we need to wrap it with an abstraction. The BlogRepository will allow us to query the data without allowing direct control over the database connection. This is what BlogRepository accomplishes for us. We can hide the details of the specific implementation, the actual context object, while surfacing a simplified API for gathering data.

The other interface that we abstracted is the IDbContext interface. This abstraction allows us to intercept tests just before they would be sent to the database. This makes the untestable part of the application as thin as possible. We can, and will, test right up to the point of database connection.

There's more...

Keeping the repository implementation clean requires us to leverage some principles and patterns that are at the core of object-oriented programming, but not specific to using Entity Framework. These principles will not only help us to write clean implementations of Entity Framework, but can also be leveraged by other areas of our code.

Dependency inversion principle

Dependency inversion is another solid principle. This states that all of the dependencies of an object should be clearly visible and passed in, or injected, to create the object. The benefit of this is two-fold: the first is exposing all of the dependencies so the effects of using a piece of code are clear to those who will use the class. The second benefit is by injecting these dependencies at construction, they allow us to unit test by passing in mocks of the dependant objects. Granular unit tests require the ability to abstract dependencies, so we can ensure only one object is under test.

Repository and caching

This repository pattern gives us the perfect area for implementing a complex or global caching mechanism. If we want to persist some value into the cache at the point of retrieval, and not retrieve it again, the repository class is the perfect location for such logic. This layer of abstraction allows us to move beyond simple implementations and start thinking about solving business problems quickly, and later extend to handle more complex scenarios as they are warranted by the requirements of the specific project. You can think of repository as a well-tested 80+% solution. Put off anything more until the last responsible moment.

Mocking

The usage of mocks is commonplace in tests because mocks allow us to verify underlying behavior without having more than one object under test. This is a fundamental piece of the puzzle for test-driven development. When you test at a unit level, you want to make sure that the level directly following the one you are testing was called correctly while not actually executing the specific implementation. This is what mocking buys us.

Where constraint

There are times when we need to create complex sets of queries which will be used frequently, but only by one or two objects. When this situation occurs, we want to reuse that code without needing to duplicate it for each object. This is where the "where" constraint helps us. It allows us to limit generically defined behavior to an object or set of objects that share a common interface or base class. The extension possibilities are near limitless.

See also

In this chapter:

  • Implementing the unit of work pattern
  • Creating mock database connections
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset