David R. Totzke

Subscribe to David R. Totzke: eMailAlertsEmail Alerts
Get David R. Totzke: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Article

The Exception Management Application Block for .NET

Your apps can gracefully handle the unexpected

In this article we will take a look at the Exception Management Application Block. Application building blocks are tested reference implementations of various application subsystems. These blocks are provided by the Patterns and Practices group at Microsoft and are designed to address common subsystem challenges across a broad range of scenarios.

Exception Handling
Up until March 2004 I supported a DOS (yes, you heard me - DOS) application that every so often would produce the following output:

Runtime Error 1 00D4: 0FFB
Press Any Key to Continue...

It would exhibit this behavior during nightly batch processing and was quite happy to display this message until I returned to the office in the morning to discover it. Nothing in the log; no hints as to the cause; nothing.

Sometimes things go wrong. I can deal with that. What bothered me most about this particular error was the fact that it was not raised by the program itself. It was a "runtime" error, i.e., caught by the runtime environment. When the program crashed, it did not fail gracefully - and it left behind no information even remotely relevant to the cause of the error. Bad program! No dessert!

Sometimes things change. You can't predict every eventuality when constructing your error handling. Something you didn't think of is bound to happen - something exceptional. At the very least, we can wrap our main message loop with a Try...Catch block to trap any exceptional conditions, clean up after ourselves, log relevant information, and exit gracefully (see Listing 1).

Not only will our program fail gracefully in the face of the unexpected, but it will also tell us what went wrong so that we can perhaps deal with it better in our next release.

Why Use the Exception Management Application Block?
The Exception Management Application Block (EMAB) for .NET provides a consistent, flexible, and extensible design for exception management. A consistent set of basic contextual information is captured for each exception through the use of the base exception class. You can derive your own custom exceptions from this base class. The ExceptionManager class provides a standard abstraction through which you can publish exceptions to multiple destinations with a minimal amount of code. Flexibility is achieved through the use of the standard .NET application configuration file and a section handler. Extensibility is provided through the IExceptionPublisher and IExceptionXmlPublisher interfaces, which allow you to create your own publishing components.

BaseApplicationException Class
The BaseApplicationException class was developed with the following design goals in mind:

  • The class should provide a base exception class from which all application exception classes can derive.
  • The base class should contain information that is valuable to all application exceptions. The base class should implement the common functionality and capture the common information, allowing all application exception classes to inherit this functionality from the base class.
  • The base class should be lightweight and should include only information that is valuable to all application exception classes.
  • The base class should be serializable to ensure that it can be remoted across .NET remoting boundaries.
During construction, the properties shown in Table 1 are populated automatically to provide basic environment data.

The ExceptionManager Class
The ExceptionManager class was developed to satisfy the following design goals:

  • The component should require a minimal amount of coding by the developer to be integrated into an application.
  • Behavior should be based on configuration settings that can be modified, allowing changes to be made without recoding or recompiling - and in the case of ASP.NET applications - without restarting the application.
  • Internal exceptions thrown by custom publishers should be caught and logged.
  • XML serialization of the exception chain is an issue. The XmlSerializer object serializes only read/write values, which limits its use for exception objects. The Exception-Manager component provides its own XmlSerializer implementation.
The ExceptionManager class is a sealed/NotInheritable class with a private constructor. It exposes a static/Shared method, Publish. A list of exception publishers can be configured in the application configuration file.

The Publish method accepts any object that is derived from System.Exception. An overloaded version of the Publish method accepts a System.Exception as well as a NameValueCollection that can be used to pass additional application-specific information. Because the Publish method is implemented as static/Shared, a developer can log exception information to all publishers with a single line of code!

catch(Exception ex)
{
//Publish the exception
ExceptionManager.Publish(ex);
}

Internal exceptions are handled by the CustomPublisher Exception class. The ExceptionManager automatically traps any exceptions thrown by a custom publisher and logs them using the default publisher.

The ExceptionManager SectionHandler implements the IConfigurationSectionHandler interface to parse the XML configuration section. The resulting object is added to the configuration collection and can be accessed by using GetConfig().

Exception Manager Configuration
The exception management section is defined in the configSections part of the application configuration file listing the associated type and assembly. The exception management section itself (see Listing 2) has a single attribute, mode, that is used to turn exception logging on and off.

Publisher Configuration
The custom publishers are listed within the ExceptionManagment section of the configuration file. The publisher has six attributes:

  1. The mode, used to turn a publisher on and off.
  2. An attribute for the assembly names associated with the custom publisher.
  3. An attribute for the type names associated with the custom publisher.
  4. A delimited list of exception types to exclude from this publisher.
  5. A list of types to include. A "+" before the type name indicates that you wish to include the type as well as any types derived from that type.
  6. If the exceptionFormat is set to "xml", the IExceptionXmlPublisher interface is used instead of the IExceptionPublisher interface.
Any custom attributes may then be added as name/value pairs. This allows developers to write custom code in the publisher to examine these attributes and take the appropriate action, as shown in the following code snippet.

    <publisher mode="on/off" assembly="AssemblyName" type="TypeName" 
               exclude="(+)Type;(+)Type" include="(+)Type;(+)Type" 
               exceptionFormat="xml" 
          customattr = "value" />

Interfaces for Extensibility
Interfaces allow us to satisfy the following design goals:

  • Developers must be able to create classes to which the exception manager can pass an exception object hierarchy and additional information.
  • Developers must also be able to create classes to which the exception manager can pass an XML representation of an exception hierarchy.
  • Updates to the Exception Management Application Block should not cause versioning issues for custom publishers using the interfaces.
IExceptionPublisher
The IExceptionPublisher interface defines a single method, Publish, that takes three parameters: a System.Exception object, a NameValueCollection object that contains additional information, and a second NameValueCollection to receive the configuration settings from the configuration file.

IExceptionXmlPublisher
The IExceptionXmlPublisher interface also defines a single method, Publish, that accepts two parameters: an XMLDocument object that is an XML representation of the exception information, and a NameValueCollection that represents the configuration settings.

DefaultPublisher Class
The ExceptionManager includes a DefaultPublisher class, which implements the IExceptionPublisher interface. The DefaultPublisher will publish the exception details to the application log if no other publishers are defined in the application's configuration file.

When to Use Custom Exception Classes
One thing you need to keep in mind when deciding whether or not to use a custom exception, or any exception for that matter, is that throwing an exception is a relatively expensive operation. For instance, you wouldn't throw an exception from an input validation routine, catch it, and then throw up a message box to the user; you'd simply alert the user. Poor input is something we are prepared for, isn't it?

That reminds me of the time a developer (don't worry Terry, I won't tell them it was you) came to me with a single input box on a Web page and told me it was bulletproof. I simply hit the Submit button. Bang! Crash! He wasn't expecting a user to submit nothing.

The System.Diagnostics namespace is a rich one that offers developers many predefined exceptions. You should spend some time looking for the right one before going off to create your own. Here are a few good reasons to create your own.

  1. An exception class does not exist for a particular situation. The YourAuraIsVeryBrownTodayExcep-tion comes to mind.
  2. A particular situation requires discrete handling. For example, you may have a case where a FileNotFoundException may be thrown, but in this particular case you want to handle it differently than you otherwise would.
  3. Specific behavior or additional information and functionality are required.
Creating a Custom Application Exception
Basic

Creating a custom application exception is easy, provided that all you need is the basic information provided for you in the base class. You simply provide the constructors and delegate to the base class, remembering to add the serializable attribute (see Listing 3).

Additional State Information
When you require additional state information you need to do a little bit more work. Because we are supporting serialization, we need to override the GetObjectData method of the base class so we can add our additional state information to the SerializationInfo object. The other thing we need to do is to provide the protected constructor for deserialization (see Listing 4).

Creating a Custom Exception Publisher
It's really quite simple. You simply provide an implementation for the IExceptionPublisher interface, which contains a single method, Publish. Take a look at the sample (available for download from www.sys-con.com/dotnet/sourcec.cfm) developed by Adam Gallant, a developer specialist with Microsoft Canada's .NET Adoption Team.

Finally
Those of you familiar with the Enterprise Instrumentation Framework (EIF) may be wondering why you should choose the EMAB over the EIF. Good question. Until recently, the EIF was available only to MSDN Universal and Enterprise subscribers. It is now available for download by all.

The EIF is a heavy framework and its primary target is distributed, loosely coupled enterprise applications. If you are building a stand-alone application, then the EIF is almost surely overkill. The framework provides support for diagnostics, and administrative and audit events. It supports call-tracing events such as database commands and internal and external calls. It can also support administrative messages as well as audit operation and message events.

The framework provides three standard event sinks.

  • TraceEventSink: This sink is appropriate for high-volume event tracing and will capture events even if the Windows Trace Session Manager service isn't running, as long as there is an active trace session running. In a distributed environment these logs must be processed on each server or be moved to a central location for consolidation.
  • LogEventSink: This sink outputs to the standard Windows Event Log.
  • WMIEventSink: This is the slowest of the standard event mechanisms on Windows 2000 systems. It should be used only for highly visible or infrequent events.

    One of the drawbacks to the EIF is that there is no default fallback. With the EMAB, if all else fails, the exception gets written to a local application log file.

    Acknowledgment

  • The author wishes to thank Microsoft Canada's Adam Gallant, a developer specialist on the .NET Adoption Team, for his gracious assistance with this article and for writing the sample code.
  • More Stories By David R. Totzke

    David R. Totzke is a Visual Developer .NET MVP, chairman of INETA's Marketing and Sponsorship Committee, and a Web application arcitect for Stryker Canada (www.stryker.ca).

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.