Thursday, February 26, 2009 2:46 PM Kazi Manzur Rashid

Implementing UnitOfWork Pattern In Linq To SQL Application

In my previous post, I have shown how to create Linq to Sql Repository which will have the maximum code coverage, In this post, I will show a simple UnitOfWork class which will flash the changes back to your database. I will be again use my ongoing UnityCommonServiceLocatorMVC project. Lets assume that in your ASP.NET MVC application you have a method in your controller which will add a category and its associate product in the Northwind database.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(string categoryName, string productName, decimal? unitPrice)
{
    Category category = new Category
                            {
                                CategoryName = categoryName
                            };

    _categoryRepository.Add(category);

    Product product = new Product
                          {
                              ProductName = productName,
                              UnitPrice = unitPrice,
                              Category = category
                          };

    _productRepository.Add(product);

    return View();
}

If you run the above code you will find nothing happens as we are not committing the changes back to the database. Now, lets create the UnitOfWork which will flush the changes:

public interface IUnitOfWork
{
    void Commit();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly IDatabase _database;

    public UnitOfWork(IDatabase database)
    {
        _database = database;
    }

    public void Commit()
    {
        _database.SubmitChanges();
    }
}

Now we can rewrite the Controller Submit method like the following which will commit the changes back to database(line 20):

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(string categoryName, string productName, decimal? unitPrice)
{
    Category category = new Category
                            {
                                CategoryName = categoryName
                            };

    _categoryRepository.Add(category);

    Product product = new Product
                          {
                              ProductName = productName,
                              UnitPrice = unitPrice,
                              Category = category
                          };

    _productRepository.Add(product);

    _unitOfWork.Commit();

    return View();
}

But still it is not convincing, I would like to show explicitly which part of my code is under the unit of work, lets do some refactoring of the UnitOfWork:

public interface IUnitOfWork : IDisposable
{
    void Commit();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly IDatabase _database;

    public UnitOfWork(IDatabase database)
    {
        _database = database;
    }

    public static IUnitOfWork Begin()
    {
        return ServiceLocator.Current.GetInstance<IUnitOfWork>();
    }

    public void Commit()
    {
        _database.SubmitChanges();
    }

    public void Dispose()
    {
    }
}

Now we can write the submit method like the following:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(string categoryName, string productName, decimal? unitPrice)
{
    using (IUnitOfWork unitOfWork = UnitOfWork.Begin())
    {
        Category category = new Category
                                {
                                    CategoryName = categoryName
                                };

        _categoryRepository.Add(category);

        Product product = new Product
                              {
                                  ProductName = productName,
                                  UnitPrice = unitPrice,
                                  Category = category
                              };

        _productRepository.Add(product);

        unitOfWork.Commit();
    }

    return RedirectToAction("Index");
}

Similar kind of Transaction.Begin() and Transaction.Commit(). Next, the unit test of controller:

public class HomeControllerTest
{
    private readonly Mock<ICategoryRepository> _categoryRepository;
    private readonly Mock<IProductRepository> _productRepository;
    private readonly Mock<IUnitOfWork> _unitOfWork;

    private readonly HomeController _controller;

    public HomeControllerTest()
    {
        _categoryRepository = new Mock<ICategoryRepository>();
        _productRepository = new Mock<IProductRepository>();
        _unitOfWork = new Mock<IUnitOfWork>();

        MockServiceLocator();

        _controller = new HomeController(_categoryRepository.Object, _productRepository.Object);
    }

    [Fact]
    public void Submit_Should_Add_Category()
    {
        Submit();

        _categoryRepository.Verify();
    }

    [Fact]
    public void Submit_Should_Add_Product()
    {
        Submit();

        _productRepository.Verify();
    }

    [Fact]
    public void Submit_Should_Flush_Changes()
    {
        Submit();

        _unitOfWork.Verify();
    }

    private void Submit()
    {
        _categoryRepository.Expect(r => r.Add(It.IsAny<Category>())).Verifiable();
        _productRepository.Expect(r => r.Add(It.IsAny<Product>())).Verifiable();
        _unitOfWork.Expect(uow => uow.Commit()).Verifiable();

        _controller.Submit("Dummy Category", "Dummy Product", 10);
    }

    private void MockServiceLocator()
    {
        var serviceLocator = new Mock<IServiceLocator>();

        serviceLocator.Expect(s => s.GetInstance<IUnitOfWork>()).Returns(_unitOfWork.Object);

        ServiceLocator.SetLocatorProvider(() => serviceLocator.Object);
    }
}

And at last the mapping of UnitOfWork:

string connectionString = ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString;

IUnityContainer container = new UnityContainer();

container.RegisterType<IDatabase, Database>(new UnityPerWebRequestLifetimeManager(), new InjectionConstructor(new [] { connectionString }))
         .RegisterType<IUnitOfWork, UnitOfWork>(new UnityPerWebRequestLifetimeManager())
         .RegisterType<ICategoryRepository, CategoryRepository>(new UnityPerWebRequestLifetimeManager())
         .RegisterType<IProductRepository, ProductRepository>(new UnityPerWebRequestLifetimeManager())
         .RegisterType<IControllerFactory, CommonServiceLocatorControllerFactory>(new ContainerControlledLifetimeManager())
         .RegisterType<IBootstrapperTask, RegisterRoutes>("route", new ContainerControlledLifetimeManager())
         .RegisterType<IBootstrapperTask, RegisterControllerFactory>("controllerFactory", new ContainerControlledLifetimeManager())
         .RegisterType<IFormsAuthentication, FormsAuthenticationService>(new ContainerControlledLifetimeManager())
         .RegisterType<IMembershipService, AccountMembershipService>(new InjectionConstructor())
         .RegisterType<AccountController>()
         .RegisterType<HomeController>();

ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));

Check that we are using the UnityPerWebRequestLifetimeManager for UnitOfWork(line 6) which we have created in the earlier post, if you set any other lifetime manager it will not work.

Download: UnitOfWork.zip

Shout it
Filed under: , , , , , , , , , , ,

Comments

No Comments