Guidance on managing exceptions

 

An exception is something you do not expect to happen in your application, there are two types of exception fatal and nonfatal.  Fatal exceptions is anything that you did not define or not based from the ApplicationException class.

 

Your applications should always define at least one or more ApplicationException types so you can inform the application of broken business rules, non fatal exceptions are not warnings they are unexpected events, to tell the caller something went wrong.

 

Let’s start with the following code for this example:

 

using System;

using System.Collections;

 

namespace blogs.msdn.com.eferron

{

  class BlogExceptionCode

  {

    [STAThread]

    static void Main(string[] args)

    {

      ArrayList listOfColors = null;

      string selectedColor = String.Empty;

 

      listOfColors = new ArrayList();

 

      listOfColors.Add("red");

      listOfColors.Add("blue");

      listOfColors.Add("green");

 

      // select a color and display it

      selectedColor = (string)listOfColors[2];

      Console.WriteLine(selectedColor);

 

      // this line throws an ArgumentOutOfRangeException

      selectedColor = (string)listOfColors[3];

      Console.WriteLine(selectedColor);

     

      Console.ReadLine();

     

    }

  }

}

 

 

The line of code that attempts to access the fourth element in the ArrayList will throw an ArgumentOutOfRangeException exception.  The code below shows a very common mistake I see in line of business applications.

try

{

  selectedColor = (string)listOfColors[3];

}

catch(Exception ex)

{

  MessageBox.Show(ex.Message);

  // ...

  // other code sometimes appear in here in different

  // customer scenarios

}

 

Console.WriteLine(selectedColor);

Console.ReadLine();

 

 The exception displayed comes from the framework, it displays some unknown cryptic message the user can do nothing about.  In your line of business applications never ship this code (code which displays a fatal exception in a MessageBox to your customers). 

 

Note: You laugh now but I have seen this time and time again.

 

The only exception type that should ever been seen in a display is one that derives from ApplicationException.  You can assure these are application specific exceptions an in many cases non fatal, that may provide the user with useful information, which does not expose sensitive system information.

 

Define at least one (in many cases several ) custom types derived from ApplicationException

 

I will define a custom exception that contains a friendly error message, which I can safely display to the customer.

 

public class ColorDoesNotExistException : ApplicationException

{

  public ColorDoesNotExistException(){}

  public ColorDoesNotExistException(string Message)

    :base(Message){}

  public ColorDoesNotExistException(string Message, Exception InnerException)

    :base(Message, InnerException){}

}

 

In effort to make the example make a little bit of refactoring is order so the example makes sense.  We will add a ColorList (server) class and FakeForm (client) class

 

public class FakeForm

{

  public FakeForm(){}

 

  public void SomeClickEvent()

  {

    ColorList colorList = null;

    string selectedColor = String.Empty;

 

    colorList = new ColorList();

    try

    {

      selectedColor = colorList.SelectColor(2);

      Console.WriteLine("You selected the color {0}", selectedColor);

 

      // this line throws the exception

      selectedColor = colorList.SelectColor(3);

      Console.WriteLine("You selected the color {0}", selectedColor);

 

    }

    catch(ArgumentOutOfRangeException systemException)

    {

      throw new ColorDoesNotExistException("Please select a valid color ...", systemException);

    }

    catch(Exception)

    {

      // this is a great place to add some debug trace statements

      // consider using trace switches for verbose logging in tough

      // debugging scenarios

      throw;

    }

  }

}

 

public class ColorList

{

  private ArrayList _listOfColors;

  public ColorList()

  {

    _listOfColors = new ArrayList();

    _listOfColors.Add("red");

 

    _listOfColors.Add("blue");

    _listOfColors.Add("green");

  }

 

  public string SelectColor(int colorIndex)

  {

    if (colorIndex < 0 || colorIndex > 2)

      throw new ArgumentOutOfRangeException("colorIndex");

 

    return (string)_listOfColors[colorIndex];

  }

}

 

Notice we catch the system exception and throw an exception based on a business rule.

 

Now for the final point: Centralized exception management:

 

In our Main method we will simulate centralized exception management.  Notice the try/catch block around the call into the client form.  I have included a few references on doing this in a Windows and Web app.  Take a look at the revised code below.

 

class BlogExceptionCode

{

  [STAThread]

  static void Main(string[] args)

  {

    FakeForm presentation = null;

    presentation = new FakeForm();

 

    // Represents our global exception management

    try

    {

      presentation.SomeClickEvent();

    }

    catch(ApplicationException nonFatalException)

    {

      // safe to display this message to the user

      // we are just indicating a business rule was broken.

      // The user can most likely fix the issue here.

      MessageBox.Show(nonFatalException.Message,"Invalid color selected");

 

      // think about instrumentation with trace switches

      // like the verbose switch for tough debugging scenarios

    }

    catch(Exception fatalException)

    {

      MessageBox.Show("Please call the help desk (800) 555-1212\nClick OK to restart the application.", "An unexpected error occurred in the BlogSampleApp.");

      // now do some type of logging to capture the fact

      // a fatal exception was generated.

      Trace.WriteLine(fatalException.Message);

    }

 

    Console.WriteLine("Press any key to exit the application");

    Console.ReadLine();

  }

}

 

Centralized exception management allows you to perform consistent exception management, and instrumentation for your entire application.  When you application encounters a fatal exception you should give the users a message letting them know the following things:

 

An unexpected error occurred

Who they can contact or where they can get help

The option to either shutdown or restart the application after clicking OK.

 

Optionally you can (should) develop a strategy to log the fatal exception or send the information to a repository so you can diagnose problems later.

 

In a windows application you can use the ThreadException event handler, in an ASP.NET application you should use the HttpApplication.Error event handler to capture exceptions in a central location.  This example will provide a few examples for WinForm applications.

 

Additional Resources

 

.NET Framework documentation - HttpApplication.Error event handler

(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWebHttpApplicationClassErrorTopic.asp)

 

MSDN Magazine - Unexpected errors

By Jason Clark

(http://msdn.microsoft.com/msdnmag/issues/04/06/NET/default.aspx)

 

ASP.NET – Global Error handling

By Jonathan Goodyear, Brian Peek, and Brad Fox

(http://www.developer.com/net/asp/article.php/961301)