ASP.NET MVC Transaction Attribute (using NHibernate)

This attribute will be applied to any action method within an MVC controller, or even to an entire MVC controller class. It will cause the entire action (or any action in the controller, depending on usage) to be executed inside of a transaction, properly committing the transaction on successful method completion.  If any unhandled exception occurred, the transaction will be rolled back.

Example Usage:

   1: /// <summary>
   2: /// Return a list of all projects
   3: /// </summary>
   4: [Transaction]
   5: public ActionResult Projects()
   6: {
   7:     var projects = ProjectRepository.GetAll();
   8:     
   9:     return View(projects);
  10: }

Implementation

To get started we are going to create an attribute that derives from ActionFilterAttribute, which is in the System.Web.Mvc namespace.  By doing this you can override any of several methods to hook into the lifecycle of your controller method.

The basic idea will be to begin the transaction before the action is executed and then commit the transaction after the action have finished executing (or rollback if there were any exceptions).

Here is the complete implementation of the TransactionAttribute class:

   1: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
   2: public class TransactionAttribute : ActionFilterAttribute
   3: {
   4:     private readonly ITransaction _currentTransaction;
   5:  
   6:     public TransactionAttribute()
   7:     {
   8:         _currentTransaction = NHibernateSessionManager.Instance.GetSession().Transaction;
   9:     }
  10:  
  11:     public override void OnActionExecuting(ActionExecutingContext filterContext)
  12:     {
  13:         _currentTransaction.Begin();
  14:     }
  15:  
  16:     public override void OnActionExecuted(ActionExecutedContext filterContext)
  17:     {
  18:         if (_currentTransaction.IsActive)
  19:         {
  20:             if (filterContext.Exception == null)
  21:             {
  22:                 _currentTransaction.Commit();
  23:             }
  24:             else
  25:             {
  26:                 _currentTransaction.Rollback();
  27:             }
  28:         }
  29:     }
  30: }

Here the OnActionExecuting method gives us a place to begin the transaction, and the OnActionExecuted method is called after the controller action has been executed (and helpfully provides us with any exception through the filterContext.Exception property).

Results

With NHibernate Profiler (which I highly recommend to anyone working with NHibernate), you can see that a transaction was started and committed around the SQL generated from the ProjectRepository.GetAll() method.  Now that is some efficient and good looking SQL.

ProjectsInATransaction

[I know, this is pretty dead simple but I am trying to work into blogging again so I am starting with the easy stuff!]

9 Comments

  • That is amazing, so where can I find source code of this sample.

  • All of the source code is in the implementation section of the post. In production sometimes I wrap the NHibernateSessionManager call to avoid a hard dependency on NHibernate, but everything you need is there.

  • How can i solve problem handling exception during updating models? My code is:

    [AcceptVerbs(HttpVerbs.Post)]
    [Transaction]
    public ActionResult Create(FormCollection collection)
    {
    try
    {
    var cat = new Cat();
    UpdateModel(cat);
    catService.Add(cat);

    return RedirectToAction("Index");
    }
    catch
    {
    //////This atribiute commit transacion on exception!?
    return View();
    }
    }

  • @benys: If you want to explicitly handle the transaction in a try/catch statement then using the Attribute is probably not the way to go. The attribute is great for wrapping an entire action in a single transaction, but will not let you do multiple transactions or modify the transaction inside the action.

    For more granular transaction support you might want to use a disposable transaction class and wrap your call in a using(var ts = new TransactionScope()) {} block. Then you can call ts.commit or ts.rollback from your code.

  • awesome, I was looking on how i would accomplish this, I prefer this to the AOP way i was going to implement.

    cheers

  • This is wrong example. Why you need transaction for select statement?

  • @codebug, See this link from NHibernate project member and NHProf creator Oren Eini: http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions.

    He discusses why you should use transactions for all database interaction, not just writes.

  • Asp net mvc transaction attribute using nhibernate.. Amazing :)

  • Asp net mvc transaction attribute using nhibernate.. Slap-up :)

Comments have been disabled for this content.