Implementing the unit of work pattern

In the next example, we present an implementation of the unit of work pattern which will allow us to limit our connections to the database, and keep the application in a stable state.

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 the data and updating it.

Open the Unit of Work 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. First, we start by adding a new unit test in the Test project to define the tests for using a unit of work pattern with the following code:
    using System;
    using System.Data.Entity.Infrastructure;
    using System.Text;
    using System.Collections.Generic;
    using System.Linq;
    using BusinessLogic;
    using DataAccess;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Rhino.Mocks;
    namespace Test
    {
      [TestClass]
      public class UnitOfWorkTest
      {
        [TestMethod]
        public void ShouldReadToDatabaseOnRead()
        {
        //Arrange
        IDbContext mockContext = MockRepository.GenerateMock<IDbContext>();
        IUnitOfWork unitOfWork = new UnitOfWork(mockContext);
        IBlogRepository repository = new BlogRepository(unitOfWork);
        //Act
        var items = repository.Set<Blog>();
        //Assert
        mockContext.AssertWasCalled(x => x.Find<Blog>());
      }
      [TestMethod]
      public void ShouldNotCommitToDatabaseOnDataChange()
      {
        //Arrange
        IDbContext mockContext = MockRepository.GenerateMock<IDbContext>();
        IUnitOfWork unitOfWork = new UnitOfWork(mockContext);
        mockContext.Stub(x => x.Find<Blog>()).Return(new List<Blog>() {new Blog() {Id = 1, Title = "Test"}}.AsQueryable());
        IBlogRepository repository = new BlogRepository(unitOfWork);
        var items = repository.Set<Blog>();
        //Act
        items.First().Title = "Not Going to be Written";
        //Assert
        mockContext.AssertWasNotCalled(x => x.SaveChanges());
        }
        [TestMethod]
        public void ShouldPullDatabaseValuesOnARollBack()
        {
          //Arrange
          IDbContext mockContext = MockRepository.GenerateMock<IDbContext>();
          IUnitOfWork unitOfWork = new UnitOfWork(mockContext);
          mockContext.Stub(x => x.Find<Blog>()).Return(new List<Blog>() { new Blog() { Id = 1, Title = "Test" } }.AsQueryable());
        IBlogRepository repository = new BlogRepository(unitOfWork);
        var items = repository.Set<Blog>();
        items.First().Title = "Not Going to be Written";
        //Act
        repository.RollbackChanges();
        //Assert
        mockContext.AssertWasNotCalled(x=>x.SaveChanges());
        mockContext.AssertWasCalled(x=>x.Rollback());
        }
        [TestMethod]
        public void ShouldCommitToDatabaseOnSaveCall()
        {
          //Arrange
          IDbContext mockContext = MockRepository.GenerateMock<IDbContext>();
          IUnitOfWork unitOfWork = new UnitOfWork(mockContext);
          mockContext.Stub(x => x.Find<Blog>()).Return(new List<Blog>() { new Blog() { Id = 1, Title = "Test" } }.AsQueryable());
        IBlogRepository repository = new BlogRepository(unitOfWork);
        var items = repository.Set<Blog>();
        items.First().Title = "Going to be Written";
        //Act
        repository.SaveChanges();
        //Assert
        mockContext.AssertWasCalled(x=>x.SaveChanges());
        }
        [TestMethod]
        public void ShouldNotCommitOnError()
        {
          //Arrange
          IDbContext mockContext = MockRepository.GenerateMock<IDbContext>();
          IUnitOfWork unitOfWork = new UnitOfWork(mockContext);
          mockContext.Stub(x => x.Find<Blog>()).Return(new List<Blog>() { new Blog() { Id = 1, Title = "Test" } }.AsQueryable());
        mockContext.Stub(x => x.SaveChanges()).Throw(new ApplicationException());
        IBlogRepository repository = new BlogRepository(unitOfWork);
        var items = repository.Set<Blog>();
        items.First().Title = "Not Going to be Written";
        //Act
        try
        {
          repository.SaveChanges();
        }
        catch (Exception)
        {
        }
        //Assert
        mockContext.AssertWasCalled(x => x.Rollback());
        }
      }
    }
  2. In the DataAccess project, create a new C# class named BlogContext with the following code:
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    using System.Linq;
    using BusinessLogic;
    namespace DataAccess
    {
      public class BlogContext : DbContext, IDbContext
      {
        public BlogContext(string connectionString)
        : base(connectionString)
        {
        }
        public DbSet<Blog> Blogs { get; set; }
        public IQueryable<T> Find<T>() where T : class
        {
          return this.Set<T>();
        }
        public void Rollback()
        {
         this.ChangeTracker.Entries().ToList().ForEach(x=>x.Reload());
        }
      }
    }
  3. In the DataAccess project, create a new C# interface called IDbContext with the following code:
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    using System.Linq;
    namespace DataAccess
    {
      public interface IDbContext
      {
        DbChangeTracker ChangeTracker { get; }
        DbSet<T> Set<T>() where T : class;
        IQueryable<T> Find<T>() where T : class;
        DbEntityEntry<T> Entry<T>(T entity) where T : class;
        int SaveChanges();
        void Rollback();
      }
    }
  4. In the DataAccess project, create a new C# interface called IUnitOfWork with the following code:
    using System;
    namespace DataAccess
    {
      public interface IUnitOfWork
      {
        void RegisterNew<T>(T entity) where T : class;
        void RegisterUnchanged<T>(T entity) where T : class;
        void RegisterChanged<T>(T entity) where T : class;
        void RegisterDeleted<T>(T entity) where T : class;
        void Refresh();
        void Commit();
        IDbContext Context { get; set; }
      }
    }
  5. In the DataAccess project, add a new C# class named UnitOfWork with the following code:
    using System.Data;
    using System.Linq;
    namespace DataAccess
    {
      public class UnitOfWork : IUnitOfWork
      {
        public IDbContext Context { get; set; }
        public UnitOfWork(IDbContext context)
        {
          Context = context;
        }
        public void RegisterNew<T>(T entity) where T : class
        {
          Context.Set<T>().Add(entity);
        }
        public void RegisterUnchanged<T>(T entity) where T : class
        {
          Context.Entry(entity).State = EntityState.Unchanged;
        }
        public void RegisterChanged<T>(T entity) where T : class
        {
          Context.Entry(entity).State = EntityState.Modified;
        }
        public void RegisterDeleted<T>(T entity) where T : class
        {
          Context.Set<T>().Remove(entity);
        }
        public void Refresh()
        {
          Context.Rollback();
        }
        public void Commit()
        {
          Context.SaveChanges();
        }
      }
    }
  6. In the BusinessLogic project, add a new C# interface named IBlogRepository with the following code:
    using System.Linq;
    namespace DataAccess
    {
      public interface IBlogRepository
      {
        IQueryable<T> Set<T>() where T : class;
        void RollbackChanges();
        void SaveChanges();
      }
    }
  7. In the DataAccess project, create a new C# class named BlogRepository with the following code:
    using System;
    using System.Data.Entity;
    using System.Linq;
    using BusinessLogic;
    namespace DataAccess
    {
      public class BlogRepository : IBlogRepository
      {
        private readonly IUnitOfWork _unitOfWork;
        public BlogRepository(IUnitOfWork unitOfWork)
        {
          _unitOfWork = unitOfWork;
        }
        public IQueryable<T> Set<T>() where T : class
        {
          return _unitOfWork.Context.Find<T>();
        }
        public void RollbackChanges()
        {
          _unitOfWork.Refresh();
        }
        public void SaveChanges()
        {
          try
          {
            _unitOfWork.Commit();
          }
          catch (Exception)
          {
            _unitOfWork.Refresh();
            throw;
          }
        }
      }
    }
  8. 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 UnitOfWork(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...

The tests set up the scenarios in which we would want to use a unit of work pattern, reading, updating, rolling back, and committing. The key to this is that these are all separate actions, not dependant on anything before or after it. If the application is web-based, this gives you a powerful tool to tie to the HTTP request so any unfinished work is cleaned up, or to ensure that you do not need to call SaveChanges since it can happen automatically.

The unit of work was originally created to track the changes made so they could be persisted, and it functions that way now. We are using a more powerful, but less recognized, feature defining the scope of the unit of work. We gave the ability to control both scope and the changes that are committed in the database in this scenario. We also have put in some clean-up which will ensure that even in the event of a failure our unit of work will try to clean up after itself before throwing the error to be handled at a higher level. We do not want to ignore these errors, but we do want to make sure they do not destroy the integrity of our database.

In addition to this tight encapsulation of work against the database, pass in our unit of work to each repository. This enables us in coupling multiple object interactions to a single unit of work. This will allow us to write code, specific to the object, without giving up the shared feature set of the database context. This is an explicit unit of work, but Entity Framework in the context defines it to give you an implicit unit of work. If you want to tie this to the HTTP request, rollback on error, or tie multiple data connections together in new and interesting ways, then you will need to code in an explicit implementation like this one.

This basic pattern will help to streamline data access, and resolve the concurrency issues caused by conflicts in the objects that are affected by a transaction.

There's more...

The unit of work is a concept which is deep at the heart of Entity Framework, and adheres, out of the box, to the principles following it. Knowing these principles, and why they are leveraged, will help us use Entity Framework to it's fullest without running into the walls built in the system on purpose.

Call per change

There is a cost for every connection to the database. If we were to make a call to keep the state in the database in sync with the state in the application, we would have thousands of calls each with connection, security, and network overhead. Limiting the number of times that we hit the database not only allows us to control this overhead, but also allows the database software to handle the larger transactions for which it was built.

Interface segregation principle

Some might be inclined to ask why we should separate unit of work from the repository pattern. Unit of work is definitely a separate responsibility from repository, and as such it is important to not only define separate classes, but also to ensure that we keep small, clear interfaces. The IDbContext interface is specific in the area of dealing with database connections through an Entity Framework object context. This allows the mocking of a context to give us testability to the lowest possible level. The IUnitOfWork interface deals with the segregation of work, and ensures that the database persistence happens only when we intend it to, ignorant of the layer under it that does the actual commands. The IRepository interface deals with selecting objects back from any type of storage, and allows us to remove all thoughts of how the database interaction happens from our dependent code base. These three objects, while related in layers, are separate concerns, and therefore need to be separate interfaces.

Refactor

We have added IUnitOfWork to our layered approach to database communication, and if we have seen anything over the hours of coding, it is code changes. We change it for many reasons, but the bottom line is that code changes often, and we need to make it easy to change. The layers of abstraction that we have added to this solution with IRepository, IUnitOfWork, and IDbContext, have all given us a point at which the change would be minimally painful, and we can leverage the interfaces in the same way. This refactoring to add abstraction levels is a core tenant of clean extensible code. Removing the concrete implementation details from related objects, and coding to an interface, forces us to encapsulate behavior and abstract our sections of code.

See also

In this chapter:

  • Testing queries
  • Implementing the repository pattern
  • Performing load testing against a database
..................Content has been hidden....................

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