views:

580

answers:

2

I'm getting annoyed with writing the following code all over the place in my MVC app.

using(var tx = new TransactionScope()){
   blah...
   tx.Complete()
}

I'd like to make this DRYer somehow.

I've thought of a couple different options for this.

  1. An action filter to declaratively mark certain actions as transactional.
  2. Override OnActionExecuting in a Controller base class and make all actions transactional in one shot.

Are either of these a good idea? Any gotcha's I should look out for? The 2nd option seems like it might be a good way to get lots of deadlocks.

Oh yeah, I'm also using StructureMap and a custom controller factory to inject deps into my controllers in case someone knows of some tricks for injecting transactions that way.

+1  A: 

In an application I am currently developing, my repositories get an NHibernate ISession from an ISessionProvider which is injected in the constructor. The session provider is an AspNetMvcSessionProvider, which will create an ISession and begin a transaction the first time ISessionProvider.OpenSession() is called, storing the ISession in the current web request.

In OnActionExecuted I manually pull the ISessionProvider out of my container and call Commit or RollBack on it, depending on whether an exception has been thrown - doing nothing of course, if no session is currently stored in the web request.

This has proven useful and I think it performs pretty well - most of the time, the application is merely reading data, which does not block other reading transactions. I have not yet experienced a database deadlock.

mookid8000
+1  A: 

You can use the Unit of Work pattern to do your transaction management. The major benefit of the unit of work pattern is that you can keep your transaction strategy on one place, or a few places when you have multiple strategies. The most simple unit of work interface could be:

public interface IUnitOfWork
{
    void Start();
    void Commit();
    void RollBack();
}

You can create various UnitOfWork implementations for different ORMs or for stored procedures or on hard-coded sql. You can start the transaction in the begin of the request. The transaction can be disposed on the end of the request. Before the disposal, you can wrap the commit in a try-catch block with rollback in the catch.

try
{
    unitOfWork.Commit();
} 
catch
{
     unitOfWork.RollBack();
     throw;
}

There transaction start strategies are:

  • Per request: one transaction is used on the whole request, this is the best way in most cases.
  • Multiple times per request: A transaction per method.
  • Per Conversation: You can create a transaction around several requests of a shopping cart checkout process.

You can manage your transaction with:

  • attributes
  • application_begin and endrequest method in global.asax
  • httpmodule

When using StructureMap, you can use Hybrid caching as InstanceScope in the unit of work configuration. You can inject the unit of work into repositories with StructureMap.

Paco