Wednesday, November 11, 2009 1:19 PM Kazi Manzur Rashid

ASP.NET MVC View and Transaction

Scott showed how to render the Grid in a Transaction. Certainly it does the job but in my opinion view component should not be responsible for this kind of cross cutting concerns, instead we can use the Action Filters. Lets see how we can utilize the Action Filter in this scenario instead of modifying the Grid code. What Scott is trying to do is encapsulate the data access operation in a transaction, the Action Filter has several methods which the ASP.NET MVC framework executes in different stages of a request. In this case, we will use the OnActionExecuting which fires before the code enters into the controller method to start a transaction and OnResultExecuted which fires when the view is processed, we will commit/rollback based upon the status, here is the code that would process the action result in a transaction:

namespace AltNorthwind
{
    using System.Web.Mvc;
    using System.Transactions;

    public class TransactionAttribute : ActionFilterAttribute
    {
        private CommittableTransaction transaction;

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            transaction = new CommittableTransaction();

            base.OnActionExecuting(filterContext);
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            base.OnResultExecuted(filterContext);

            try
            {
                if ((filterContext.Exception != null) && (!filterContext.ExceptionHandled))
                {
                    transaction.Rollback();
                }
                else
                {
                    transaction.Commit();
                }
            }
            finally
            {
                transaction.Dispose();
            }
        }
    }
}

Next, we will decorate the Controller Action method with this attribute like the following:

[Transaction]
public ActionResult Index()
{
    return View(repository.All());
}

Hope, you would find it useful.

Shout it
Filed under: , , , ,

Comments

# re: ASP.NET MVC View and Transaction

Wednesday, November 11, 2009 3:39 AM by Edmund

Hi,

Assume you have this sequence in your controller:

(1 )operation X

(2) exception

(3) operation Y

If the exception is not handled, the transaction Rollback never occurs and the system is left with operation X executed and not rolled back.

Edmund

# re: ASP.NET MVC View and Transaction

Wednesday, November 11, 2009 3:22 PM by Paco

This will create seperate transaction when you use things like RenderAction in views. Why don't you use global.asax.beginrequest to start a transaction?

# re: ASP.NET MVC View and Transaction

Wednesday, November 11, 2009 4:48 PM by Augi

I agree that OnActionExecuting/OnResultExecuted is better solution then Grid modification. But your solution is absolutelly wrong and doesn't work! There is only one instance of attribute class so your private "transaction" member is always the same and this will not work when there will be more requests in parallel. You should save current transaction into some storage that is unique for each request - i.e. you should use ViewData dictionary with some unique key (ugly but works).

# re: ASP.NET MVC View and Transaction

Wednesday, November 11, 2009 5:01 PM by Augi

I'm so sorry! I have thought that GetCustomAttributes gets the same instance (that they are cached). I'm wrong :(

# re: ASP.NET MVC View and Transaction

Wednesday, November 11, 2009 8:13 PM by CharlieK

In order to get this to work with LINQ to SQL, I had to add this line after the new CommittableTransaction() call:

Transaction.Current = transaction;

After the commit and rollback statements I also had to add:

Transaction.Current = null;

You could also preserve the existing transaction  as shown here (but I think it will always be null if this is run in an ActionFilter):

msdn.microsoft.com/.../ms172146.aspx

Thanks for the great post, it helped me better understand filters and that their memory exists for the duration of the ActionResult call.

# re: ASP.NET MVC View and Transaction

Wednesday, November 11, 2009 8:47 PM by Charlie

Per Edmund's suggestion I added the following to handle the case where an error is thrown during the Controller Action Executing:

public override void OnActionExecuted(ActionExecutedContext filterContext)

       {

           if ((filterContext.Exception != null) && (!filterContext.ExceptionHandled))

           {

               transaction.Rollback();

               Transaction.Current = null;

           }

       }

# re: ASP.NET MVC View and Transaction

Thursday, November 12, 2009 1:40 AM by Edmund

Hi,

Combining CharlieK's and Charlie's last comments, I got this to work in my LINQ2SQL MVC solution.

Thanks !

Edmund

# re: ASP.NET MVC View and Transaction

Thursday, November 12, 2009 8:58 AM by Billy McCafferty

We take a very similar approach within S#arp Architecture using NHibernate.  The code for the attribute is at github.com/.../TransactionAttribute.cs

# re: ASP.NET MVC View and Transaction

Saturday, November 14, 2009 5:10 PM by Fabio Maulo

Amen!!