WEBSITE

ASP.NET 4 : Error Handling, Logging, and Tracing - Handling Exceptions

10/14/2013 7:42:12 PM

The first line of defense in an application is to check for potential error conditions before performing an operation. For example, a program can explicitly check whether the divisor is 0 before performing a calculation or whether a file exists before attempting to open it:

If Divisor <> 0 Then
' Safe to divide some number by Divisor.
End If

If System.IO.File.Exists("myfile.txt") Then
' You can now open the myfile.txt file.
' However, you should still use exception handling because a variety of
' problems can intervene (insufficient rights, hardware failure, etc.).
End If

Even if you perform this basic level of "quality assurance," your application is still vulnerable. For example, you have no way to protect against all the possible file access problems that occur, including hardware failures or network problems that could arise spontaneously in the middle of an operation. Similarly, you have no way to validate a user ID and password for a database before attempting to open a connection—and even if you did, that technique would be subject to its own set of potential errors. In some cases, it may not be practical to perform the full range of defensive checks, because they may impose a noticeable performance drag on your application. For all these reasons, you need a way to detect and deal with errors when they occur.

The solution is structured exception handling. To use structured exception handling, you wrap potentially problematic code in the special block structure shown here:

Try
' Risky code goes here (opening a file, connecting to a database, and so on).
Catch
' An error has been detected. You can deal with it here.
Finally
' Time to clean up, regardless of whether or not there was an error.
End Try

The Try statement enables error handling. Any exceptions that occur in the following lines can be "caught" automatically. The code in the Catch block will be executed when an error is detected. And either way, whether a bug occurs or not, the Finally block of the code will be executed last. This allows you to perform some basic cleanup, such as closing a database connection. The Finally code is important because it will execute even if an error has occurred that will prevent the program from continuing. In other words, if an unrecoverable exception halts your application, you'll still have the chance to release resources.

The act of catching an exception neutralizes it. If all you want to do is render a specific error harmless, you don't even need to add any code in the Catch block of your error handler. Usually, however, this portion of the code will be used to report the error to the user or log it for future reference. In a separate component (such as a business object), this code might handle the exception, perform some cleanup, and then rethrow it to the calling code, which will be in the best position to remedy it or alert the user. Or it might actually create a new exception object with additional information and throw that.

1. Catching Specific Exceptions

Structured exception handling is particularly flexible because it allows you to catch specific types of exceptions. To do so, you add multiple Catch statements, each one identifying the type of exception (and providing a new variable to catch it in), as follows:

Try
' Database code goes here.
Catch err As System.Data.SqlClient.SqlException
' Catches common database problems like connection errors.
Catch err As System.NullReferenceException
' Catches problems resulting from an uninitialized object.
End Try

An exception will be caught as long as it's an instance of the indicated class or if it's derived from that class. In other words, if you use this statement:

Catch err As Exception

you will catch any exception, because every exception object is derived from the System.Exception base class.

Exception blocks work a little like conditional code. As soon as a matching exception handler is found, the appropriate Catch code is invoked. Therefore, you must organize your Catch statements from most specific to least specific:

Try
' Database code goes here.
Catch err As System.Data.SqlClient.SqlException
' Catches common database problems like connection errors.
Catch err As System.NullReferenceException
' Catches problems resulting from an uninitialized object.
Catch err As System.Exception
' Catches any other errors.
End Try

Ending with a Catch statement for the base Exception class is often a good idea to make sure no errors slip through. However, in component-based programming, you should make sure you intercept only those exceptions you can deal with or recover from. Otherwise, it's better to let the calling code catch the original error.

DETERMINING THE EXCEPTIONS YOU NEED TO CATCH

When you're using classes from the .NET Framework, you may not know what exceptions you need to catch. Fortunately, the Visual Studio Help can fill you in.

The trick is to look up the method or constructor you're using in the class library reference. One fast way to jump to a specific method is to use the Help index—just type in the class name, followed by a period, followed by the method name, as in File.Open . If there is more than one overloaded version of the method, you'll see a page that lists them all, and you'll need to click the one that has the parameters you want.

Once you find the right method, scroll through the method documentation until you find a section named Exceptions. This section lists all the possible exceptions that this method can throw. For example, if you look up the File.Open() method, you'll find that possible exceptions include DirectoryNotFoundException, FileNotFoundException, UnauthorizedAccessException, and so on. You probably won't write a Catch block for each possible exception. However, you should still know about all of them so you can decide which exceptions you want to handle separately.


2. Nested Exception Handlers

When an exception is thrown, .NET tries to find a matching Catch statement in the current method. If the code isn't in a local structured exception block or if none of the Catch statements matches the exception, .NET will move up the call stack one level at a time, searching for active exception handlers.

Consider the example shown here, where the Page.Load event handler calls a private DivideNumbers() method:

Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As EventArgs) Handles Me.Load
Try
DivideNumbers(5, 0)
Catch err As DivideByZeroException
' Report error here.
End Try
End Sub

Private Function DivideNumbers(ByVal number As Decimal, _
ByVal divisor As Decimal) As Decimal
Return number/divisor
End Function

In this example, the DivideNumbers() method lacks any sort of exception handler. However, the DivideNumbers() method call is made inside a Try block, which means the problem will be caught further upstream in the calling code. This is a good approach because the DivideNumbers() routine could be used in a variety of circumstances (or if it's part of a component, in a variety of different types of applications). It really has no access to any kind of user interface and can't directly report an error. Only the calling code is in a position to determine whether the problem is a serious one or a minor one, and only the calling code can prompt the user for more information or report error details in the web page.

NOTE

In this example, great care is taken to use the Decimal data type rather than the more common Double data type. That's because contrary to what you might expect, it is acceptable to divide a Double by 0. The result is the special value Double.PositiveInfinity (or Double.NegativeInfinity if you divide a negative number by 0).

You can also overlap exception handlers in such a way that different exception handlers filter out different types of problems. Here's one such example:

Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As EventArgs) Handles Me.Load
Try
Dim Average As Integer = GetAverageCost(DateTime.Now)
Catch err As DivideByZeroException
' Report error here.
End Try
End Sub

Private Function GetAverageCost(saleDate As Date) As Integer
Try
' Use Database access code here to retrieve all the sale records
' for this date, and calculate the average.
Catch err As System.Data.SqlClient.SqlException
' Handle a database related problem.
Finally
' Close the database connection.
End Try
End Function

2.1. Dissecting the Code . . .

You should be aware of the following points:

  • If an SqlException occurs during the database operation, it will be caught in the GetAverageCost() method.

  • If a DivideByZeroException occurs (for example, the method receives no records but still attempts to calculate an average), the exception will be caught in the calling Page.Load event handler.

  • If another problem occurs (such as a null reference exception), no active exception handler exists to catch it. In this case, .NET will search through the entire call stack without finding a matching Catch statement in an active exception handler and will generate a runtime error, end the program, and return a page with exception information.

3. Exception Handling in Action

You can use a simple program to test exceptions and see what sort of information is retrieved. This program allows a user to enter two values and attempts to divide them. It then reports all the related exception information in the page (see Figure 1).

Figure 1. Catching and displaying exception information

Obviously, you can easily prevent this exception from occurring by using extra code-safety checks, or you can elegantly resolve it using the validation controls. However, this code provides a good example of how you can deal with the properties of an exception object. It also gives you a good idea about what sort of information will be returned.

Here's the page class code for this example:

Public Partial Class ErrorHandlingTest
Inherits System.Web.UI.Page

Protected Sub cmdCompute_Click(ByVal sender As Object, _
ByVal e As EventArgs) Handles cmdCompute.Click

Try
Dim A, B, Result As Decimal
A = Decimal.Parse(txtA.Text)
B = Decimal.Parse(txtB.Text)
Result = A / B
lblResult.Text = Result.ToString()
lblResult.ForeColor = System.Drawing.Color.Black
Catch err As Exception
lblResult.Text = "<b>Message:</b> " & err.Message & "<br /><br />"
lblResult.Text &= "<b>Source:</b> " & err.Source & "<br /><br />"
lblResult.Text &= "<b>Stack Trace:</b> " & err.StackTrace
lblResult.ForeColor = System.Drawing.Color.Red
End Try
End Sub

End Class


Note that as soon as the error occurs, execution is transferred to an exception handler. The code in the Try block isn't completed. It's for that reason that the result for the label is set in the Try block. These lines will be executed only if the division code runs error-free.


4. Mastering Exceptions

Keep in mind these points when working with structured exception handling:


Break down your code into multiple Try/Catch blocks:

If you put all your code into one exception handler, you'll have trouble determining where the problem occurred. You have no way to "resume" the code in a Try block. This means that if an error occurs at the beginning of a lengthy Try block, you'll skip a large amount of code. The rule of thumb is to use one exception handler for one related task (such as opening a file and retrieving information).


Report all errors:

During debugging, portions of your application's error handling code may mask easily correctable mistakes in your application. To prevent this from happening, make sure you report all errors, and consider leaving out some error handling logic in early builds.


Don't use exception handlers for every statement:

Simple code statements (assigning a constant value to a variable, interacting with a control, and so on) may cause errors during development testing but will not cause any future problems once perfected. Error handling should be used when you're accessing an outside resource or dealing with supplied data that you have no control over (and thus may be invalid).

Other  
  •  ASP.NET 4 : Error Handling, Logging, and Tracing - Exception Handling
  •  ASP.NET 4 : Error Handling, Logging, and Tracing - Common Errors
  •  Sharepoint 2010 : Designing a Workflow Using Visio 2010 (part 3) - Using Visio Services to Visualize Workflow State
  •  Sharepoint 2010 : Designing a Workflow Using Visio 2010 (part 2) - Implementing a Visio Workflow Using SharePoint Designer
  •  Sharepoint 2010 : Designing a Workflow Using Visio 2010 (part 1) - Using the Microsoft SharePoint Workflow Template
  •  Sharepoint 2010 : Workflow - Demonstration Scenario
  •  Sharepoint 2010 : Workflow Foundation Fundamentals
  •  Sharepoint 2013 : Understanding project sites (part 2) - Managing tasks
  •  Sharepoint 2013 : Understanding project sites (part 1)
  •  Sharepoint 2013 : Creating team sites (part 4) - Using a team notebook, Using a site mailbox
  •  
    Top 10
    Review : Sigma 24mm f/1.4 DG HSM Art
    Review : Canon EF11-24mm f/4L USM
    Review : Creative Sound Blaster Roar 2
    Review : Philips Fidelio M2L
    Review : Alienware 17 - Dell's Alienware laptops
    Review Smartwatch : Wellograph
    Review : Xiaomi Redmi 2
    Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
    Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
    3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
    REVIEW
    - First look: Apple Watch

    - 3 Tips for Maintaining Your Cell Phone Battery (part 1)

    - 3 Tips for Maintaining Your Cell Phone Battery (part 2)
    VIDEO TUTORIAL
    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
    Popular Tags
    Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8