PetaPoco – Quick and easy Unit of Work

A simple, lightweight implementation of the Unit of Work pattern for PetaPoco micro ORM.

The Unit of Work pattern

If you have been using any of the big Object Relational Mappers out there you have almost certainly been using the Unit of Work pattern. For example NHibernate has ITransaction and Entity Framework has ObjectContext or DbContext. Martin Fowler says that the Unit of Work pattern:

Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.

A Unit of Work is used to track changes to the application domain caused by loading and saving data to the persistence layer. You would generally need a Unit of Work to cover a business transaction to ensure that all the changes made in the business transaction are persisted correctly (or not persisted at all). There are lots of other clever things that the Unit of Work inside your ORM will do, such as minimising DB calls by maintaining lists of changes to objects.

As PetaPoco is a Micro ORM it makes sense to have a micro Unit of Work, which is in the spirit of its heavyweight cousins whilst remaining lightweight.

Why would I want to use a Unit of Work?

Lets say that you have an application that has a service layer which talks to a repository. There will be a time that you want to have a transaction that spans multiple database calls (usually updates and deletes) as part of a service operation. As you are a diligent developer you also like to unit test your service methods and so need to be able to isolate the service method. The service method should not know what kind of persistence the Unit of Work is wrapping as our application layers should be loosely coupled.

Therefore our simple Unit of Work must:

  • Be able to support a transaction at business logic (service) level,
  • Be mockable to isolate the service for testing,
  • Allow the service layer to be loosely coupled to the repository

The basic PetaPoco transaction looks like this:

// Create a PetaPoco database object
var db = new PetaPoco.Database("connectionStringName");

using (var scope=db.Transaction)
{
    // Do transacted updates here

    // Commit
    scope.Complete();
}

This is not ideal as we have a concrete dependency on PetaPoco.Database so it will make testing awkward. Also we do not want our service layer to know about PetaPoco Database objects. We need to wrap this up somehow so it can be injected into the service via the IOC container of our choice.

Implementation

The implementation has four parts:

IUnitOfWork

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

This interface exposes only a Commit method to call in order to persist the transaction and a getter for the PetaPoco database object so we can do something useful with it. There is no need for a rollback method as PetaPoco does not require it. If the Commit method is not called then the transaction is rolled back.

IUnitOfWorkProvider

public interface IUnitOfWorkProvider {
    IUnitOfWork GetUnitOfWork();
}

The Unit of Work Provider has a single method to give us an instance of the Unit of Work. It is needed in order to avoid creating a new instance of the UnitOfWork implementation inside the service. This will become more clear when we see how the Unit of Work is used in the service. Importantly we can mock this in the tests and return a mock Unit of Work to use in the tests.

PetaPocoUnitOfWorkProvider

public class PetaPocoUnitOfWorkProvider : IUnitOfWorkProvider
{
    public IUnitOfWork GetUnitOfWork()
    {
        return new PetaPocoUnitOfWork();
    }
}

This implementation of IUnitOfWorkProvider returns a new instance of PetaPocoUnitOfWork.

PetaPocoUnitOfWork

public class PetaPocoUnitOfWork : IUnitOfWork {
    private readonly Transaction _petaTransaction;
    private readonly Database _db;

    public PetaPocoUnitOfWork() {
        db = new Database("DbName");
        _petaTransaction = new Transaction(_db);
    }

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

    public Database Db {
        get { return _db; }
    }

    public void Commit() {
        _petaTransaction.Complete();
    }
}

The PetaPocoUnitOfWork is just a simple wrapper for PetaPoco’s own transaction mechanism. The constructor creates a new PetaPoco Database object for the named database and starts a new transaction on the Database object. The commit method marks the transaction as complete. As mentioned earlier, there is no explicit rollback command. Instead the transaction is rolled back if the transaction.Complete method has not been called.  We expose the Database object so we can do something useful with it. We are also implementing the Dispose method from the IDisposable interface which enables us to use wrap the UnitOfWork usage in a using statement.

Using the Unit of Work

Inside the service method the unit of work is used like this:

using (var uow = unitOfWorkProvider.GetUnitOfWork()) {

    repository.InsertNewProduct(uow, newProduct);

    repository.UpdateStockLevel(uow, newProduct, newStockLevel);

    uow.Commit();
}

We start a Unit of Work by calling the GetUnitOfWork method on unitofWorkProvider, which has been injected into the service via an IOC container. This keeps the unit of work completely decoupled from the service layer, which knows nothing about the inner working of the Unit of Work.

Once the Unit of Work has been established it is simply a matter of calling repository methods passing in the newly created Unit of Work, ensuring that commit is called upon successful completion of the repository methods. All the repository calls that have the UnitOfWork passed as an argument will use the same PetaPoco Database object with the same transaction. As long as all the repository methods complete without an exception the transaction will be committed. You should obviously apply try and catch were appropriate.

A repository method would look something like this:

public void InsertNewProduct(IUnitOfWork unitOfWork, Product product)
{ 
    //Do some stuff

    //Perform some db work on the unit of work
    unitOfWork.Db.Update(...);    
}

Your repository method should be a bit more comprehensive, maybe returning the number of updated rows instead of null and having some error checking, but this should give you the idea of how the Unit of Work is used within your repository.

When it comes to unit testing your service it is a simple matter of mocking the IUnitOFWorkProvider for the service constructor and having the GetUnitOfWork method return a mock IUnitOfWork.

Advertisements

14 Responses to PetaPoco – Quick and easy Unit of Work

  1. xiao says:

    Do you still wrap the unit of work around select statements? If so do you do a commit still?

    • jheppinstall says:

      Hi xiao, thanks for your question. As a unit of work is a way to group a number of database operations into an atomic unit that either succeeds as a whole, or fails, it is not usually necessary to use a unit of work for select statements as they do not have side effects.

      In the case of this simple unit of work is essentially wrapper for a transaction, and it would not be usual practice to use a transaction for a select.

      • xiao says:

        hmm I come from a nhibernate background where everything has to be in transaction even select statements. If I where not to wrap it in a unit of work through then that becomes a problem for mocking as would you not be doing like in the examples then.

        // Create a PetaPoco database object
        var db=new PetaPoco.Database(“connectionStringName”);

        // Show all articles
        foreach (var a in db.Query(“SELECT * FROM articles”))
        {
        Console.WriteLine(“{0} – {1}”, a.article_id, a.title);
        }

    • jheppinstall says:

      As I understand it, nHibernate’s UOW has a lot more to do as it needs to keep track of the materialised objects and their state, so this may well be the case. With PetaPoco using a UOW is not necessary for select.

      The key to testability with your example would be to have a repository method along the lines of blogRepository.GetAllArticles(); which could be mocked. All the PetaPoco data access would be contained within the repository.

      • xiao says:

        Hmm. Ya I could do that but would that not just make the code more inconsistent? In a repository you probably will have both select and insert/update/delete statements. Kinda of seems weird in if some cases you will be bring in the unitOfWork into the method and then using it’s petaPoco data access but then in some cases you would have in the repository it’s own petaPoco data access that is only used for select statements?

      • jheppinstall says:

        Of course you could use a UOW with selects if you feel it makes sense for you. For me using a UOW says something about the intent of the calls within the UOW using statement. As with a lot of things in IT, there is no right or wrong answer in this case.

      • xiao says:

        if possible I would like to see what your repos and server layers look like when your doing select statements. I probably will wrap them in a transaction as it won’t change the outcome and it is something I am use to seeing. I do like to see other peoples takes on things though.

      • jheppinstall says:

        A select repo method would be something like:

        Public List GetAllUsers()
        {
        return new Database(“database”).Fetch(“SELECT * FROM Users);
        }

        The service call would be simply:

        var users = userRepository.GetAllUsers();

  2. xiao says:

    Also the IUnitWork maybe mocked but the Database property will not be mocked as it is not a interface so that will throw an exception.

    • jheppinstall says:

      Hi xiao. When we are unit testing our service layer, we are mocking the IUnitOFWorkProvider to provide a mock unit of work, so that when var uow = unitOfWorkProvider.GetUnitOfWork() is called during the test we can get an IUnitOfWork to pass to our mocked repository method. The Database property is never called on the mocked IUnitOfWork as this is called in the repository not the service.

      There is limited utility in testing repository methods, as you correctly state it is difficult to achieve the required isolation even with mocking. Repository testing is best left to integration level tests.

      • xiao says:

        Right. I see what you mean. Even though I am also using repo pattern as well I had this in my service layer “unitOfWork.Db.OneTimeCommandTimeout = 500;” I guess I could move this also in the unit of work as a separate method that sets the timeout as petapoco default is 30 second what is not along enough for one of my queries.

    • jheppinstall says:

      I think you are right. You should never refer to unitOfWork.Db at the service level, only within the repository. I am not sure if you are using a repository and service layer, bit would greatly simplify your testing. My post on TDD with MVC shows the basics of using this type of approach.

      • xiao says:

        Yes I am using both repository and service layers for the current project and actually trying to do TDD as well. I agree the unitOfWork.Db should not be used in the service layer but as you see it can be very easy to do my mistake 🙂 . As with the TimeOut I showed you I think that should go in the unitOfWork as this is more a setting and I don’t think an individual repo method should determine how long the timeout is but the transaction should(assuming you wrap even select statements in a transaction as I am more leaning too) as I am thinking if it is in each repo method then the timeout just gets burried and if we move it a separate setting that is set in the repo for all the method then we run into each repo having that piece of code.

      • jheppinstall says:

        It would be fine to set the timeout in UOW as long as the setting is suitable for all the calls made with that UOW. If you had a really long running operation that required a really long time out you would have to decide if that would be a good value to have for all calls. You could always set the timeout as a default in the UOW and override it in a particular repository method if it required an exceptional value. It is generally good practice to keep timeouts as low as practically possible.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: