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.
[I know, this is pretty dead simple but I am trying to work into blogging again so I am starting with the easy stuff!]