0
votes

I'm following this example on how to implement session-per-request transactions in NHibernate.

I've got the following:

public class SessionManagementAttribute : ActionFilterAttribute
{
    private ISessionFactory SessionFactory { get; set; }

    public SessionManagementAttribute()
    {
        SessionFactory = WebApiApplication.SessionFactory;
    }

    public override void OnActionExecuting(ActionExecutingContext actionContext)
    {
        var session = SessionFactory.OpenSession();
        CurrentSessionContext.Bind(session);
        session.BeginTransaction();
    }

    public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
    {
        var session = SessionFactory.GetCurrentSession();
        var transaction = session.Transaction;
        if (transaction != null && transaction.IsActive)
        {
            //  TODO: Do I need to rollback a transaction here?
            transaction.Commit();
        }
        session = CurrentSessionContext.Unbind(SessionFactory);
        session.Close();
    }
}

I'm wondering -- if my transaction commit fails and an NHibernate exception is thrown -- should I be catching that and rolling back the transaction? In my current code I have an explicit catch and rollback. For example,

public void Save(Video video)
{
    try
    {
        NHibernateSessionManager.Instance.BeginTransaction();
        DoSave(video);
        NHibernateSessionManager.Instance.CommitTransaction();
    }
    catch (Exception exception)
    {
        Logger.Error(exception);
        NHibernateSessionManager.Instance.RollbackTransaction();
        throw;
    }
}

but I've seen hints through my readings that this might not be necessary. Can anyone clarify?

1

1 Answers

0
votes

We always have to handle the session. Either if all goes ok, but even if something goes wrong. Take a look at these: 6.5. Lazy Initialization (cite):

In a web-based application, an event handler can be used to close() the ISession only at the very end of a user request, once the rendering of the view is complete. Of course, this places heavy demands upon the correctness of the exception handling of your application infrastructure. It is vitally important that the ISession is closed and the transaction ended before returning to the user, even when an exception occurs during rendering of the view. The event handler has to be able to access the ISession for this approach.

Another place: 9.2. Loading an object (cite)

... Note that Load() will throw an unrecoverable exception if there is no matching database row.

But mostly the:

9.8. Exception handling

An extract:

If the ISession throws an exception you should immediately rollback the transaction, call ISession.Close() and discard the ISession instance. Certain methods of ISession will not leave the session in a consistent state...

Documented example:

ISession sess = factory.openSession();
try
{
    // do some work
    ...
    sess.Flush();
    currentTransaction.Commit();
}
catch (Exception e)
{
    currentTransaction.Rollback();
    throw;
}
finally
{
    sess.Close();
}

Summary

Other words, session handling is our responsibility. We have to be sure that the rollback is called regardles some "external circumstances" which could do rollback on connection close... We also have to be sure that the session is correctly exposed at the end