I'm currently using an Open Session in View pattern in an ASP.NET WebForms application (adapted quite a bit from Billy McCafferty's http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx). To oversimply state what goes on here:
- Open NHibernate transaction at beginning of request
- Commit at end of request (rolling back on any errors)
I usually handle any exceptional database errors by capturing them in Application_Error (where I log, redirect to generic error page, etc.) as follows:
...
if (Context != null && Context.IsCustomErrorEnabled)
{
Server.Transfer(ErrorPageLocation, false);
}
else
{
log.Error("Unhandled Exception trapped in Global.asax", exception);
}
but with NHibernate, by the time I get to any NHibernate/Database errors, it's too late in the request to do my usual Server.Transfer (obviously no transferring going to happen this late in the request) error redirecting (although logging still takes place). As a very quick fix, I've done the following in my custom HttpModule's HttpApplication Context EndRequest:
try
{
// Commits any open transaction and throws after rolling back any HibernateException and closing session.
NHibernateSessionManager.Instance.CommitTransaction();
}
catch (HibernateException)
{
HttpContext.Current.Response.Redirect(ERROR_PAGE_LOCATION);
}
finally
{
NHibernateSessionManager.Instance.CloseSession();
}
...but this smells not only because I'm now referencing NHibernate in my Web, but mostly because I'm sure there must be a better way. What would be a better way of dealing with redirecting the user to a generic error page in the event of a database/NHibernate error where, as any exceptions thrown at this point is thrown too late in the process to get handled further back in the request with a Server.Transfer in Global.asax.cs Application_Error? Taking it one step further, with web services it gets even trickier as the above hack obviously has no effect (and no exception gets thrown to be handled on the receiving end - in my case, most typically in client-side ajax calls).
Please tell me I'm missing something obvious (usually the obvious hits me right after I hit submit on questions like these)!