Intercepting Business Transactions

In Patterns of Enterprise Application Architecture, the Unit of Work design pattern is defined as:

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

NHibernate seems to have a great implementation of the unit of work, but understanding when to start and commit the unit of work without repeating yourself can be a little tricky. One thing we’ve been doing is starting a unit of work using an interceptor.

[Interceptor(typeof (IUnitOfWorkInterceptor))]
public class AccountTasks : IAccountTasks
{
    public bool are_valid(ICredentials credentials)
    {
        ...
    }
}

Account tasks is a service layer piece, that is decorated with an interceptor that will begin and commit a unit of work.

public interface IUnitOfWorkInterceptor : IInterceptor
{}

public class UnitOfWorkInterceptor : IUnitOfWorkInterceptor
{
    private readonly IUnitOfWorkFactory factory;

    public UnitOfWorkInterceptor(IUnitOfWorkFactory factory)
    {
        this.factory = factory;
    }

    public void Intercept(IInvocation invocation)
    {
        using (var unit_of_work = factory.create()) {
            invocation.Proceed();
            unit_of_work.commit();
        }
    }
}

The interceptor starts a new unit of work, before proceeding with the invocation. If no exceptions are raised the unit of work is committed. If a unit of work is already started, the unit of work factory returns an empty unit of work. This ensures that if a service layer method calls into another method that it doesn’t start another unit of work.

public interface IUnitOfWorkFactory : IFactory<IUnitOfWork>
{}

public class UnitOfWorkFactory : IUnitOfWorkFactory
{
    private readonly IApplicationContext context;
    private readonly IDatabaseSessionFactory factory;
    private readonly TypedKey<ISession> key;

    public UnitOfWorkFactory(IApplicationContext context, IDatabaseSessionFactory factory)
    {
        this.context = context;
        this.factory = factory;
        key = new TypedKey<ISession>();
    }

    public IUnitOfWork Create()
    {
        if (unit_of_work_is_already_started()) {
            return new EmptyUnitOfWork();
        }

        return create_a_unit_of_work().start();
    }

    private bool unit_of_work_is_already_started()
    {
        return context.contains(key);
    }

    private IUnitOfWork create_a_unit_of_work()
    {
        var session = factory.create();
        context.add(key, session);
        return new UnitOfWork(session, context);
    }
}

The implementation of the repository pulls the active session from the application context.

public class DatabaseRepository<T> : IRepository<T>
{
    private readonly IApplicationContext context;
    private readonly IKey<ISession> session_key;

    public DatabaseRepository(IApplicationContext context)
    {
        this.context = context;
        session_key = new TypedKey<ISession>();
    }

    public IQueryable<T> all()
    {
        return the_current_session().Linq<T>();
    }

    public void save(T item)
    {
        the_current_session().SaveOrUpdate(item);
    }

    public void delete(T item)
    {
        the_current_session().Delete(item);
    }

    private ISession the_current_session()
    {
        var current_session = context.get_value_for(session_key);
        if (null == current_session || !current_session.IsOpen) {
            throw new NHibernateSessionNotOpenException();
        }
        return current_session;
    }
}

For more information on Interceptors check out the Castle stack…

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Mo Khan

mO, is just a kid who's excited about writing software. He's a student of his profession, and just wants to share his thoughts on software development.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

3 Responses to Intercepting Business Transactions

  1. Colin Jack says:

    Have you ever used an interceptor to ensure that the aggregates involved were valid before committing, if so how did you find it?

  2. joshka says:

    posted on one of your other blogs (I think this post ended up 3 times in my reader). Can you talk about the Application Context and IKey stuff a little in a future post?

  3. Mo says:

    @Colin I have never thought to try that. I would be interested to see what that might look like.

    @joshka Hopefully my most recent post, answers your question.
    http://www.lostechies.com/blogs/mokhan/archive/2008/11/04/opening-doors.aspx

    I apologize for puking all over your RSS feed. I’m not sure how that happened.