Handle Exception at Various Levels in ASP.Net Application

This article will save you from a “Yellow Screen Of Death” (YSOD). Yes, ASP.NET developers call this unhandled exception page that name. I hope in your developer life you have seen this page many times, that’s fine in the development environment but what if it happens in a production server?

From your customer to your boss (if your boss is not a technical person) all will feel that, Oh, the application is worthless and there is a very big mistake its development, our money and organization’s name will shrink. Here is a sample YSOD for you.

Exception

We developer people are more or less familiar with the technical jargon scattered within this screen. But how will an end user what has happened in the application? Most probably they will be very confused and there is a greater chance that you might receive an unsatisfied mail from your client.

So, the root cause of everything is this yellow screen. Why not replace this ugly screen with a polite message? Yes, we will do that, actually the purpose of this article is to never ever see this yellow page. But at first we will learn various levels of the exception handling mechanism. The term level is purposefully used. There are three levels for tracking our exceptions in ASP.NET applications.

  • Block level
  • Page Level
  • Application Level

Block Level

This is the most granular level of exception handling in applications. We are very familiar with try-catch blocks that we can wrap over a set of code and if an exception occurs then it will handle this exception. The exception handling block is like this.

  1. try  
  2. {  
  3.     throw new InsufficientMemoryException();  
  4. }  
  5. catch (Exception ex)  
  6. {  
  7.     throw new Exception("");  
  8. }  
  9. catch  
  10. {  
  11.     throw;  
  12. }   

Since this is not a basic exception handling article, we will not go deeper into that example.

Page Level

We can implement an exception handling mechanism at the page level. The advantage is that we do not need to wrap all our code within the try-catch-finally block; we can say that, this is centralizing the exception handling mechanism of a page.

Wherever exception bubbles up, it will catch by following code.
  1. void Page_Error(object sender, EventArgs e)  
  2. {  
  3.     Exception ex = Server.GetLastError();  
  4. }

To determine the Exception type we need to use the GetLastError() function of the Server class that will return the exception type object and by checking this object we can make further decisions, whether to handle the exception within this page or throw it to a higher level. If a higher level then what higher level?

Application Level

Yes, this is the top level of the exception handle mechanism. If we implement it in the Global.asax page. We all are familiar with the Global.asax page that mostly deals with the Global (application) and session related objects. If we implement the Application_Error() event within this page then all unhandled exceptions will stack here and we can make a supreme decision about the error. Whether we will ignore it or log it or send it in mail. Here is the implementation of Application_Error() in a Global.asax page.

  1. protected void Application_Error(object sender, EventArgs e)  
  2. {  
  3.     //Catch Exception in Central place  
  4.     Exception exceptionObject = Server.GetLastError();  
  5. }

The Exception detection mechanism is very similar to the Page Level exception handling mechanism. We are using the GetLastError() function of the Server class that will return one exception type object. Now, it’s our duty to investigate the exception type further and the possible solution for it.

Fine, we have seen the three levels of the exception handling mechanism in ASP.NET applications. Now, the obvious question is, shall we implement all those levels in a single application? Why not? They can peacefully adjust themselves within the same application and moreover they work like a team. If one fails to handle an exception then the other tries. If again it fails then its higher authority tries, like this.

Fine, the next question is if the bottom level is able to solve the exception? Will it call to the higher level? No, if the lower level is capable of handling an exception then it will not bubble up (call) an exception to its higher level. Let’s clear up the concept.

In one ASP.NET page I have written this code.

  1. try  
  2. {  
  3.   throw new DivideByZeroException();  
  4. }  
  5. catch (DivideByZeroException ex)  
  6. {  
  7. }  
  8. catch  
  9. {  
  10.     throw;  
  11. } 

Where we are throwing a DivideByZero Exception from a try block and next to that we are catching it by passing a proper Exception object type. So the catch block can handle the exception. Now make the situation a bit more complex. Within this page we will keep the following code for the global exception handling in the page level.

  1. void Page_Error(object sender, EventArgs e)  
  2. {  
  3.     Exception ex = Server.GetLastError();  
  4. } 

And we will see that the code will not execute at the time of the exception handling. Because the native try-catch block is sufficient to handle the exception, no need to call its superior mechanism and for the same reason the Application Level exception mechanism will be skipped, if any.

Fine, so far we have learned the levels of the exception mechanism and clarified a few concepts of it. We will now implement all those levels in one sample application and we will fulfill our goal. Let’s proceed step-by-step.

Step 1: Create exception in block level and wrap with try-catch

The step is pretty simple and we have done it many times. Within a try block we are throwing an InsufficientMemoryException and we are assuming that this try block will throw “DivideByZeroException” , if our assumption is right then we can handle this exception otherwise we will throw it to a higher level.
  1. try  
  2. {  
  3.     throw new InsufficientMemoryException();  
  4. }  
  5. catch (DivideByZeroException ex)  
  6. {  
  7.    //show Message  
  8. }  
  9. catch  
  10. {  
  11.     throw;  
  12. } 

Where is the higher level mechanism? The page level. We will implement it in the next step.

Step 2: Implement exception handling mechanism in page level

Here is the next level of exception handling. We have implemented a Page_Error() event that can capture all unhandled exceptions within this page. Now, we are expecting this page to only throw an ObjectDisposeException and a little bit optimistically we have kept the mechanism to handle only this type of exception. (That is a very silly implementation, we should not do that.). Anyway, if the exception is any other type then we will throw it to a higher handling mechanism.

  1. void Page_Error(object sender, EventArgs e)  
  2. {  
  3.     Exception ex = Server.GetLastError();  
  4.     if (ex is ObjectDisposedException)  
  5.     {  
  6.        //Log this Exception  
  7.     }  
  8.     else  
  9.     throw new HttpUnhandledException("Unhandle Exception", ex);  
  10. } 

The next higher authority is the Application Level exception handling mechanism.

Step 3: Implement Application_Error() in Global.asax page

  1. protected void Application_Error(object sender, EventArgs e)  
  2. {  
  3.     //Catch Exception in Global place  
  4.     Exception exceptionObject = Server.GetLastError();  
  5.     try  
  6.     {  
  7.     if (exceptionObject != null)  
  8.     {  
  9.        if (exceptionObject.InnerException != null)  
  10.        {  
  11.           exceptionObject = exceptionObject.InnerException;  
  12.        }  
  13.        switch (exceptionObject.GetType().ToString())  
  14.        {  
  15.          case "System.Threading.ThreadAbortException":  
  16.          HttpContext.Current.Server.ClearError();  
  17.          break;  
  18.          default:  
  19.          LogException(exceptionObject);//Custom method to log error  
  20.          break;  
  21.       }  
  22.    }  
  23. }  
  24. catch { }//Avoiding further exception from exception handling  
  25. } 

And the LogException() function is a stub for implementing much more stuff by going beyond our core explanation. Here it is.

  1. public static void LogException(Exception ex)  
  2. {  
  3.     //Log my Exception  
  4. } 

Step 4: Configure web.config file

  1. <customErrors mode="On">  
  2.   <error statusCode="500" redirect="errorpage.aspx"/>  
  3. </customErrors> 

We have set the customError mode=On which means that we are interested in handling the unhandled exception with our own logic. And within that we have set an error page name for when it sends statuscode = 500 that is nothing but an internal server error.

We know that there are many more error types in the application and we can customize each error page for each type of exception. Just to keep it simple, we have given only one type of error page.

Ok, we are done with all the configuration and settings, now we will run this application and by setting a breakpoint we will observe the execution flow. So, set the first breakpoint at the native try-catch level.

throw Exception

In the native catch level we had expected only a DivideByZero exception and since the actual exception is not the same, it will throw it to  higher level.

DivideByZero exception

At the Page Level, we have implemented the exception handling mechanism only for an ObjectDisposeException, since the actual exception is not the same we are throwing it to the next higher level.

Application Error event

It has now reached an Application_Error() event in the Global.asax page. And we are seeing that the error object ob LogException() function has been called from an Application_Error() event.

And finally the polite error message appears on the screen. (Oh, it’s not well decorated, but much better than the yellow screen.)

error message

Conclusion

This article has shown the various levels of Exception Handling in ASP.NET applications. The reason for writing this article is, a few days ago I was thinking that if we can handle all exceptions in a Global.asax page then which the language developer guys have developed try-catch-finally block.

Now, I hope, you too understood why they have implemented try-catch blocks.


Similar Articles