Dowemo


Question:

I use nHibernate for ORM and Ninject for IoC. I create nHibernate sessions per some custom scope (which you can assume is per request). I begin the transaction onActivation. I commit the transaction onDeactivation.

The problem is that if an exception happens during the request I want to rollback the transaction rather than committing it. Any idea how to detect (in a clean way, most probably using Ninject Context) that an exception has happened?

Note: I am not concerned about the exceptions that can happen on commit which I can catch in the following code and role back easily.

protected void BindWithSessionWrapper<T>(Func<IContext, T> creationFunc) where T : ISessionWrapper


{


    Bind<T>().ToMethod(creationFunc)


        .InScope(x => new NinjectCustomScope()) // work in progress !!!


        .OnActivation(t => t.Session.BeginTransaction(IsolationLevel.ReadCommitted))


        .OnDeactivation((c, t) => 


            { 


                t.Session.Transaction.Commit();


                t.Session.Dispose();


            });


}


Update:

I followed the suggestion by @BatteryBackupUnit. So I added the following to the Error EventHandler:

    Error += (s, e) =>


        {


            HttpContext.Current.Items["ErrorRaised"] = true;


        };


And I modified the OnDeactivation to look like this:

OnDeactivation(t => 


                    { 


                        if ((bool?)HttpContext.Current.Items["ErrorRaised"] == true)


                            t.Session.Transaction.Rollback();


                        else


                            t.Session.Transaction.Commit();



                        t.Session.Dispose();


                    });


It works fine, but that would be better if Ninject would take care of this by setting a flag in the Context if an exception happened :)


Best Answer:


How about implementing an IHTTPModule and subscribing to the Error event? Like described here

In the Error event handler, use System.Web.Mvc.DependencyResolver.Current.GetService(typeof (ISession)) to retrieve the current session and rollback the transaction.

Note, however, that in case the request did not use a session, this will create one, which is quite superfluous.

You might do something like checking whether a transaction was started and only then rolling it back. But you'd still create a session unnecessarily.

You could further improve that by using the Error event handler to set a flag on HttpContext.Current.Items, like

HttpContext.Current.Items["RollbackTransaction"] = true;


and then use it in the OnDeactivation of the session like:

    .OnDeactivation((c, t) => 


        { 


            if(HttpContext.Current.Items.Contains("RollbackTransaction"])


            {


                t.Session.Transaction.Rollback();


            }


            else


            {


                t.Session.Transaction.Commit();


            }


            t.Session.Dispose();


        });


Please note that HttpContext is thread local, that means when you switch threads it may be null or -worst case - it might even be another HttpContext.

Please also note that i was unable to try it out so it may not work. Feedback appreciated.




Copyright © 2011 Dowemo All rights reserved.    Creative Commons   AboutUs