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.

1 [Interceptor(typeof (IUnitOfWorkInterceptor))]
2   public class AccountTasks : IAccountTasks
3   {
4       public bool are_valid(ICredentials credentials)
5       {
6           ...
7       }
8   }

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

 1 public interface IUnitOfWorkInterceptor : IInterceptor
 2   {
 3   }
 4 
 5   public class UnitOfWorkInterceptor : IUnitOfWorkInterceptor
 6   {
 7       private readonly IUnitOfWorkFactory factory;
 8 
 9       public UnitOfWorkInterceptor(IUnitOfWorkFactory factory)
10       {
11           this.factory = factory;
12       }
13 
14       public void Intercept(IInvocation invocation)
15       {
16           using (var unit_of_work = factory.create()) {
17               invocation.Proceed();
18               unit_of_work.commit();
19           }
20       }
21   }

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.

 1 public interface IUnitOfWorkFactory : IFactory<IUnitOfWork>
 2   {
 3   }
 4 
 5   public class UnitOfWorkFactory : IUnitOfWorkFactory
 6   {
 7       private readonly IApplicationContext context;
 8       private readonly IDatabaseSessionFactory factory;
 9       private readonly TypedKey<ISession> key;
10 
11       public UnitOfWorkFactory(IApplicationContext context, IDatabaseSessionFactory factory)
12       {
13           this.context = context;
14           this.factory = factory;
15           key = new TypedKey<ISession>();
16       }
17 
18       public IUnitOfWork create()
19       {
20           if (unit_of_work_is_already_started()) {
21               return new EmptyUnitOfWork();
22           }
23 
24           return create_a_unit_of_work().start();
25       }
26 
27       private bool unit_of_work_is_already_started()
28       {
29           return context.contains(key);
30       }
31 
32       private IUnitOfWork create_a_unit_of_work()
33       {
34           var session = factory.create();
35           context.add(key, session);
36           return new UnitOfWork(session, context);
37       }
38   }

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

 1 public class DatabaseRepository<T> : IRepository<T>
 2   {
 3       private readonly IApplicationContext context;
 4       private readonly IKey<ISession> session_key;
 5 
 6       public DatabaseRepository(IApplicationContext context)
 7       {
 8           this.context = context;
 9           session_key = new TypedKey<ISession>();
10       }
11 
12       public IQueryable<T> all()
13       {
14           return the_current_session().Linq<T>();
15       }
16 
17       public void save(T item)
18       {
19           the_current_session().SaveOrUpdate(item);
20       }
21 
22       public void delete(T item)
23       {
24           the_current_session().Delete(item);
25       }
26 
27       private ISession the_current_session()
28       {
29           var current_session = context.get_value_for(session_key);
30           if (null == current_session || !current_session.IsOpen) {
31               throw new NHibernateSessionNotOpenException();
32           }
33           return current_session;
34       }
35   }
For more information on Interceptors check out the Castle stack...
comments powered by Disqus