Dowemo
0 0 0 0


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