NHibernate throws "Invalid operation. The connection was closed." exception - asp.net

I have a code piece to record user data in Session_End event of Global.asax. It is working but sometimes it throws "Invalid operation. The connection was closed" exception. I have failed to replicate this situation on development server. It only occurs on application server. What is wrong? Thanks.
protected void Session_End(object sender, EventArgs e)
{
try
{
userlog = UserLog.LoadBySessionAndLogoutTime(NHibernateHTTPModule.CurrentSession, Session.SessionID, null);
userlog.LogoutTime = DateTime.Now;
UserLog.Update(NHibernateHTTPModule.CurrentSession, userlog);
}
catch (Exception exception)
{
Mail.SendMail("Error", error);
}
}

It would help if you explain where you open and close your nhibernate session. Unless you say the opposite I'll assume that you use "Open Session in View" pattern.
The user session ends outside of the scope of a request so if you open a nhibernate session on each request it won't be available when you try to log.
You should verify if you have an opened nhibernate session before logging. If none is active, you'll need to open a new one.

Chances are an Exception relating to an NHibernate operation was thrown somewhere in your code. Any NHibernate exception will invalidate the session.
So there is some condition on the application server that is different from your dev environment that contributes to the error. You could try logging it on the application server:
protected void Application_Error()
{
Exception myError = null;
if(HttpContext.Current.Server.GetLastError() != null)
{
// log error
myError = Server.GetLastError();
....
}
}
in global.asax will intercept all errors so you can log them.

Related

Exception Handling in DAO layer of Spring Project with JPA

I am using Spring & JPA with Hiberate as vendor.
In DAO layer I am trying to persist entity. If duplicate exists, it throws ConstraintViolationException of Hibernate. I have written try-catch in DAO for catching the exception but it does't go to catch block at all and throws Exception to service layer.
Does JPA allow to catch JDBCException in DAO layer or it'll directly throw it to upper layer?
My code looks like as follows :
public void saveEntity(SomeEntity entity) throws CustomException {
try {
... do something...
entityManager.persist(entity);
}catch(Exception e) {
throw new CustomException(e.getMessage());
}
}
Here If something happens before persist it catches but if something goes wrong while persisting and JDBCException comes it doesn't.
I know that I could have avoided this situation by checking first that if record exists and if not, only then I'll save. But I want to know why JDBCException (or any database related exceptions) exceptions are not getting caught here.
Any help appreciated.
I have found the reason for this.
The catch block in the DAO class is not able to catch exception because transaction commit is happening after method getting executed and so the method on which I have put #Transactional (service layer method) is catching the exception instead.
Here if I use flush() right after persist, it tries to commit right there and throws Exception which will get caught.
public void saveEntity(SomeEntity entity) throws CustomException {
try {
... do something...
entityManager.persist(entity);
entityManager.flush(); //tries to commit here & throws ConstraintViolationException if already exists
}catch(Exception e) {
throw new CustomException(e.getMessage());
}
}

Using ELMAH and Application_Error at the same time

We have an ASP.NET MVC 3 application that is configured to use ELMAH. We also have code in our Application_Error method like this. Both ELMAH and our custom code log to a database.
protected void Application_Error(object sender, EventArgs e)
{
MvcApplication app = (MvcApplication)sender;
HttpContext httpContext = app.Context;
Exception ex = app.Server.GetLastError();
HttpException httpException = ex as HttpException;
//log the error with our custom logging
Server.ClearError();
if (httpContext.IsCustomErrorEnabled) //only show custom error if enabled in config
{
httpContext.Response.Clear();
httpContext.ClearError();
//show our own custom error page here
}
}
The problem (not really a problem, but whatever) we see is that both ELMAH and our custom code log the exception to the DB. I would expect calls to Server.ClearError() and httpContext.ClearError would handle the error and it would never get to ELMAH. But, does the fact that the error is being logged twice imply that ELMAH and application_error are running basically in parallel and they both receive the unhandled exception at the same time? If so is there anyway to tell ELMAH to ignore the error?
Our intent is to only have ELMAH handle an error if something goes REALLY wrong, like in the ASP.NET pipeline after elmah is registered, but before the MVC application would be running.
The issue is that it is logging to ELMAH first. So yes you can tell it not to log to ELMAH using e.Dismiss(): The ErrorLog_Filtering function below is hit before Application_Error. So add this function and any needed logic you need to determine if you want it in ELMAH or not.
void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e)
{
//get the exceptions like:
Exception m = e.Exception;
Exception ex = e.Exception.GetBaseException();
//tell it not to log the error in ELMAH like (based on whatever criteria you have):
e.Dismiss();
//Application_Error will be hit next
}
protected void Application_Error(object sender, EventArgs e)
{
//your logic
}

asp.net + exceptions and redirecting

My intention is to log an error(I am using Log4Net) when an exception is caught and redirect to a pretty looking page with some error message. I have a class that returns a Type T object , mostly a DataSet.
In my Catch statement I wrote this, it works but I am not sure if there's a more appropriate way of handling, can someone please advice. Thanks. Note that the throw cannot be omitted because the class has a return type.:
catch (Exception ex)
{
log.Error(ex);
HttpContext.Current.Response.Redirect("~/errorPage.aspx");
throw ex;
}
It depends upon how you want to handle error on the page, In general , unhandled exception should be bubbled up to application_error in gloabl.asax file to it generic.Here is one simple way to handle this error.
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
// Get the exception object.
Exception exc = Server.GetLastError();
// Handle HTTP errors
if (exc.GetType() == typeof(HttpException))
{
// The Complete Error Handling Example generates
// some errors using URLs with "NoCatch" in them;
// ignore these here to simulate what would happen
// if a global.asax handler were not implemented.
if (exc.Message.Contains("NoCatch") || exc.Message.Contains("maxUrlLength"))
return;
//Redirect HTTP errors to HttpError page
Server.Transfer("HttpErrorPage.aspx");
}
// For other kinds of errors give the user some information
// but stay on the default page
Response.Write("<h2>Global Page Error</h2>\n");
Response.Write(
"<p>" + exc.Message + "</p>\n");
Response.Write("Return to the <a href='Default.aspx'>" +
"Default Page</a>\n");
// Log the exception and notify system operators
ExceptionUtility.LogException(exc, "DefaultPage");
ExceptionUtility.NotifySystemOps(exc);
// Clear the error from the server
Server.ClearError();
}

Transaction disappears when using windsor + nhibernate

I'm using Castle Windsor 3.0 for dependency injection in a demo ASP.NET app. One of my controllers takes an ICustomerService instance, which in turn takes an ISession instance, all via constructor. The ISession is registered with Windsor using a factory method and PerWebRequest life style.
_container.Register(Component.For<ISessionFactory>().Instance(DbHelper.BuildSessionFactory()).LifestyleSingleton());
_container.Register(Component.For<ISession>().LifestylePerWebRequest().UsingFactoryMethod(x => x.Resolve<ISessionFactory>().OpenSession()));
In the global.asax file, I have an Application_EndRequest handler that attempts to commit the transaction:
protected void Application_EndRequest(object sender, EventArgs e)
{
if (!IsStaticResourceRequest())
{
var app = (HttpApplication)sender;
var factory = _container.Resolve<ISessionFactory>();
var session = ManagedWebSessionContext.Unbind(Context, factory);
if (session != null &&
session.Transaction != null &&
session.Transaction.IsActive)
{
session.Transaction.Commit();
session.Transaction.Dispose();
session.Dispose();
}
}
}
The problem is that the PerWebRequest lifestyle of Windsor has its own Application_EndRequest event handler which disposes of the service prior to my Application_EndRequest handler (in global.asax) executing, so the code in my Application_EndRequest handler never gets a chance to commit the transaction. Is there a workaround for this?
I moved away from this pattern and rather have the commit happen in your controller action. Why wait until you have left the page and at the end of the request before committing. Makes passing UI messages a lot more difficult. Currently I take in the ISession as a param in the controller to ensure it gets injected:
public void SomeController(ISession session)
{
_session = session;
}
then in your Action:
using(var trans = new _session.BeginTransaction()){
try{
..update etc
trans.commit();
}
catch(Exception ex){
trans.rollback();
// return message, log etc
}
}
Windsor will cleanup the session for you at the end of the request.
The solution ended up being committing my transaction in an event earlier in the ASP.NET lifecycle. I picked ReleaseRequestState, but any of the methods leading up to the EndRequest event should be adequate, as long as the handler has finished processing the request.

Filtering signalled exceptions out of Elmah error mails

I've implemented a wrapper method around Elmah's Error Signaling, where a raised exception is only seen by Elmah for logging and mailing, but now I'd like to filter only these signalled exceptions out of the mailing, but still log them. How can I do this?
This is my simple wrapper:
public void LogException(Exception exception)
{
ErrorSignal.FromContext(HttpContext.Current).Raise(exception);
}
I've thought of wrapping the input exception in a custom SignalledException, and filtering those out of mailing, but then my logs get full of SignalledExceptions, and not the real exceptions.
Any other ideas please?
Elmah filtering works mainly on exception types. Programmatic filtering would allow me to examine information added to an exception, but I have yet to come across an Exception type with an ElmahFilteringData property, and I prefer not to 'invade' the exceptions I want logged. Here is how I accomplished only sending email notificatins for certain signalled exceptions:
First I have a special wrapper exception, purely for telling Elmah not to send an email notification for exceptions of this type:
public class ElmahEMailBlockWrapperException: Exception
{
public const string Explanation = "This is a wrapper exception that will be blocked by Elmah email filtering. The real exception is the InnerException";
public ElmahEMailBlockWrapperException(Exception wrappedException):base(Explanation, wrappedException) {}
}
Then, when I raise an exception I normally only want logged and not emailed, but maybe sometimes emailed, I use this code in my exception logging service:
public void LogException(Exception exception, bool includeEmail)
{
if (includeEmail)
{
ErrorSignal.FromContext(HttpContext.Current).Raise(exception);
}
else
{
// Wrap the input exception in a special exception type that the Elmah email filter can block.
var wrappedException = new ElmahEMailBlockWrapperException(exception);
ErrorSignal.FromContext(HttpContext.Current).Raise(wrappedException);
}
}
Now, in the Elmah filter events in Global.asax, I unwrap the exception to log it, and if it is wrapped, dismiss it from the email notification pipeline:
public void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e)
{
// If the exception was wrapped in a ElmahEMailBlockWrapperException exception to be blocked by the ErrorMail filter, the InnerException
// of the the ElmahEMailBlockWrapperException is the real exception to be logged, so we extract it, log it, and dismiss the wrapper.
var ebw = e.Exception.GetBaseException() as ElmahEMailBlockWrapperException;
if (ebw != null)
{
ErrorLog.GetDefault(HttpContext.Current).Log(new Error(ebw.InnerException));
e.Dismiss();
}
}
public void ErrorMail_Filtering(object sender, ExceptionFilterEventArgs e)
{
// If the exception was wrapped, i.e. raised only to be logged by Elmah and not emailed, dismiss it.
if (e.Exception.GetBaseException() is ElmahEMailBlockWrapperException)
{
e.Dismiss();
}
}

Resources