NHibernate Transactional Boundaries
In NHibernate Bootstrapping with StructureMap, I did not address the issue of transactional boundaries. The example had very simple controller actions that managed their own commits. However, this question on StackOverflow left me wondering who should be responsible for the commit?
Sharp Architecture’s Approach
Sharp Architecture is an excellent “architectural foundation for building maintainable web applications with ASP.NET MVC”. It has an Action Filter called Transaction that starts a transaction in the OnActionExecuting event, and then commits if no exception occurred. Otherwise, it explicitly rolls the transaction back. In fact, the Implicit transactions thread in the Sharp Architecture Google group discusses the Transaction attribute and some of the issues using it. The Transaction attribute provides the functionality that I want, but it requires the developer to decorate each Action method with an attribute. Why not make the HttpModule that creates and disposes of the Unit of Work handle the commit?
Modified Bootstrapping Example
I have modified the Bootstrapping NHibernate with StructureMap example from my previous post to do just that. Now, the NHibernateModule is responsible for calling Commit() on the UnitOfWork. Here is the NHibernateModule’s new Dispose method:
public void Dispose() { _unitOfWork.Commit(); _unitOfWork.Dispose(); }
The only addition is the call to the _unitOfWork.Commit(). However, now that the NHibernateModule is responsible for the commit, the developer can not abort an existing transaction. So, I added a Rollback() method to the IUnitOfWork class. The concrete implementation, UnitOfWork, calls _transaction.Rollback().
One final change to the UnitOfWork prevents it from throwing an exception if the developer explicitly calls commit by checking the ITransaction’s IsActive property before attempting to commit. Here is the UnitOfWork’s new Commit method:
public void Commit() { if (_transaction.IsActive) _transaction.Commit(); }
Multi-Transaction Unit of Work
There is still one issue nagging at me. Once the developer commits the unit of work, the transaction is closed. The FubuTasks example in the fubumvc-contrib project solves this problem by starting a new transaction once the existing transaction is committed. Here is the source for FubuTask’s NHibernateUnitOfWork. I have talked to Chad Myers about this implementation and he has moved away from the idea of handling rollback in general. In any event, I don’t like the idea of starting a new transaction because I don’t think there should be one transaction per request. At the same time, I am preventing the developer from using my unit of work for more than one transaction. At least for now, opening a new transaction is a YAGNI for me. What do you think?
The full source code with these changes are available in the mvbalaw-commons project here.
Tags: NHibernate
Reader Comments (3)
I remember Ayende has been having certain problems with using a HttpModule. Something like calling Dispose several times per request, or similar. Anyway, keeping all initialization/finalization logic in a single place (HttpApplication) looks cleaner, what you think?
Hi
One transaction per request is not possible for some situations. Example: creating new invoice need generate new number. Number generator use database. Generator should quick generate new number and commit transaction. Then we can create new invoice. That 2 transactions per request.
You shouldn't holding generator lock within transaction too long.
Yes, you said well that The only addition is the call to the _unitOfWork.Commit(). However, now that the NHibernateModule is responsible for the commit, the developer can not abort an existing transaction.