次の方法で共有


3 - Error Management Made Exceptionally Easy: Using the Exception Handling Application Block

patterns & practices Developer Center

On this page: Download:
Introduction | When Should I Use the Exception Handling Block? | How Do I Use the Exception Handling Block? | What Exception Policies Do I Need? | Allowing Exceptions to Propagate | About Exception Handling Policies | Choosing an Exception Handling Strategy | Process or HandleException? - Using the Process Method, Using the HandleException Method | Diving in with a Simple Example | Applying Exception Shielding | Wrapping an Exception | Configuring the Wrap Handler Policy | Initializing the Exception Handling Block | Editing the Application Code to Use the New Policy | Replacing an Exception | Logging an Exception | Shielding Exceptions at WCF Service Boundaries | Creating a Fault Contract | Configuring the Exception Handling Policy | Editing the Service Code to Use the New Policy | The Fault Contract Exception Handler | Handling Specific Exception Types | Executing Code around Exception Handling | Assisting Administrators | Extending Your Exception Handling | Summary | More Information

Download code

Download PDF

Download Paperback

Introduction

Let's face it, exception handling isn't the most exciting part of writing application code. In fact, you could probably say that managing exceptions is one of those necessary tasks that absorb effort without seeming to add anything useful to your exciting new application. So why would you worry about spending time and effort actually designing a strategy for managing exceptions? Surely there are much more important things you could be doing.

In fact, a robust and well-planned exception handling plan is a vital feature of your application design and implementation. It should not be an afterthought. If you don't have a plan, you can find yourself trying to track down all kinds of strange effects and unexpected behavior in your code. And, worse than that, you may even be sacrificing security and leaving your application and systems open to attack. For example, a failure may expose error messages containing sensitive information such as: "Hi, the application just failed, but here’s the name of the server and the database connection string it was using at the time." Not a great plan.

The general expectations for exception handling are to present a clear and appropriate message to users, and to provide assistance for operators, administrators, and support staff who must resolve problems that arise. For example, the following actions are usually part of a comprehensive exception handling strategy:

  • Notifying the user with a friendly message
  • Storing details of the exception in a production log or other repository
  • Alerting the customer service team to the error
  • Assisting support staff in cross-referencing the exception and tracing the cause
Dn440728.note(en-us,PandP.60).gifJana says:
Jana Applications need to deliver notifications to different groups of people such as end users, operators, and support staff. Different types and levels of information are appropriate to these different groups.

So, having decided that you probably should implement some kind of structured exception handling strategy in your code, how do you go about it? A good starting point, as usual, is to see if there are any recommendations in the form of well-known patterns that you can implement. In this case, there are. The primary pattern that helps you to build secure applications is called Exception Shielding. Exception Shielding is the process of ensuring that your application does not leak sensitive information, no matter what runtime or system event may occur to interrupt normal operation. And on a more granular level, it can prevent your assets from being revealed across layer, tier, process, or service boundaries.

Two more exception handling patterns that you should consider implementing are the Exception Logging pattern and the Exception Translation pattern. The Exception Logging pattern can help you diagnose and troubleshoot errors, audit user actions, and track malicious activity and security issues. The Exception Translation pattern describes wrapping exceptions within other exceptions specific to a layer to ensure that they actually reflect user or code actions within the layer at that time, and not some miscellaneous details that may not be useful.

In this chapter, you will see how the Enterprise Library Exception Handling block can help you to implement these patterns, and become familiar with the other techniques that make up a comprehensive exception management strategy. You'll see how to replace, wrap, and log exceptions; and how to modify exception messages to make them more useful. And, as a bonus, you'll see how you can easily implement exception shielding for Windows Communication Foundation (WCF) Web services.

When Should I Use the Exception Handling Block?

The Exception Handling block allows you to configure how you want to manage exceptions, and centralize your exception handling code. It provides a selection of plug-in exception handlers and formatters that you can use, and you can even create your own custom implementations. You can use the block when you want to implement exception shielding, modify exceptions in a range of ways, or chain exceptions (for example, by logging an exception and then passing it to another layer of your application). The configurable approach means that administrators can change the behavior of the exception management mechanism simply by editing the application configuration without requiring any changes to the code, recompilation, or redeployment.

Dn440728.note(en-us,PandP.60).gifCarlos says:
Carlos The Exception Handling block was never intended for use everywhere that you catch exceptions. The block is primarily designed to simplify exception handling and exception management at your application or layer boundaries.

How Do I Use the Exception Handling Block?

Like all of the Enterprise Library application blocks, you start by configuring your application to use the block, as demonstrated in Chapter 1, "Introduction." You can add the block to your project by using the NuGet package manager in Visual Studio: search online in the Manage Nuget Packages dialog for EnterpriseLibrary.ExceptionHandling. Then you add one or more exception policies and, for each policy, specify the type of exception it applies to. Finally, you add one or more exception handlers to each policy. The simplest approach is a policy that specifies the base type, Exception, and uses one of the handlers provided with the block. However, you'll see the various handlers, and other options, demonstrated in the examples in this chapter.

There are two additional NuGet packages that you may choose to install. The EnterpriseLibrary.ExceptionHandling.Logging package enables you to log formatted exception information using the Logging Application Block, and the EnterpriseLibrary.ExceptionHandling.WCF package that includes a WCF provider. The section "Shielding Exceptions at WCF Service Boundaries" later in this chapter describes the features of this WCF provider.

What Exception Policies Do I Need?

The key to handling exceptions is to apply the appropriate policies to each type of exception. You can pretend you are playing the well-known TV quiz game that just might make you a millionaire:

Question: How should I handle exceptions?


A: Wrap them

B: Replace them

C: Log and re-throw them

D: Allow them to propagate

Dn440728.note(en-us,PandP.60).gifJana says:
Jana The options in the table are the most common options, but they are not the only options. For example, you might have custom handlers, you might decide not to re-throw an exception after logging, or you might decide to simply swallow some exceptions.

You can, of course, phone a friend or ask the audience if you think it will help. However, unlike most quiz games, all of the answers are actually correct (which is why we don't offer prizes). If you answered A, B, or C, you can move on to the section "About Exception Handling Policies." However, if you answered D: Allow them to propagate, read the following section.

Allowing Exceptions to Propagate

If you cannot do anything useful when an exception occurs, such as logging exception information, modifying the exception details, or retrying the failed process, there is no point in catching the exception in the first place. Instead, you just allow it to propagate up through the call stack, and catch it elsewhere in your code—either to resolve the issue or to display error messages. Of course, at this point, you can apply an exception policy; and so you come back to how you should choose and implement an appropriate exception handling strategy.

Dn440728.note(en-us,PandP.60).gifBeth says:
Beth You should consider carefully whether you should catch an exception at a particular level in your application or allow it to propagate up through the call stack. You should log all exceptions that propagate to the top of the call stack and shut the application down as gracefully as possible. For a more detailed discussion, see Eric Lippert’s "Vexing exceptions" blog post and Raymond Chen’s "Cleaner, more elegant, and harder to recognize" blog post.

About Exception Handling Policies

Each policy you configure for the Exception Handling block can specify one or more exception types, such as DivideByZeroException, SqlException, InvalidCastException, the base class Exception, or any custom exception type you create that inherits from System.Exception. The block compares exceptions that it handles with each of these types, and chooses the one that is most specific in the class hierarchy.

For each policy, you configure:

  • One or more exception handlers that the block will execute when a matching exception occurs. You can choose from four out-of-the-box handlers: the Replace handler, the Wrap handler, the Logging handler, and the Fault Contract exception handler. Alternatively, you can create custom exception handlers and choose these (see "Extending your Exception Handling" near the end of this chapter for more information).
  • A post-handling action value that specifies what happens after the Exception Handling block executes the handlers you specify. Effectively, this setting tells the calling code whether to continue executing. You can choose from:
    • NotifyRethrow (the default). Return true to the calling code to indicate that it should rethrow the original exception. This allows the exception to keep its original stack trace.
    • ThrowNewException. The Exception Handling block will throw the exception that results from executing all of the handlers.
    • None. Returns false to the calling code to indicate that it should continue executing.

The following code sample shows how to define a policy named MyTestExceptionPolicy programmatically. This policy handles the three exception types—FormatException, Exception, and InvalidCastException—and contains a mix of handlers for each exception type. You must configure a LogWriter instance before you add a Logging exception handler to your exception handling.

var policies = new List<ExceptionPolicyDefinition>();
 
var myTestExceptionPolicy = new List<ExceptionPolicyEntry>
  {
      {
          new ExceptionPolicyEntry(typeof (FormatException),
              PostHandlingAction.NotifyRethrow,
              new IExceptionHandler[]
                {
                  new LoggingExceptionHandler(...),
                  new ReplaceHandler(...)
                })
      },
      {
         new ExceptionPolicyEntry(typeof (InvalidCastException),
              PostHandlingAction.NotifyRethrow,
              new IExceptionHandler[]
                {
                  new LoggingExceptionHandler(...),
                })
      },
      {
          new ExceptionPolicyEntry(typeof (Exception),
              PostHandlingAction.NotifyRethrow,
              new IExceptionHandler[]
                {
                  new ReplaceHandler(...)
                })
      }
   };
 
policies.Add(new ExceptionPolicyDefinition(
  "MyTestExceptionPolicy", myTestExceptionPolicy));
ExceptionManager manager =  new ExceptionManager(policies);

The following code sample shows the details of a ReplaceHandler object. Notice how you can specify the properties for each type of exception handler. For example, you can see that the Replace Handler has properties for the exception message and the type of exception you want to use to replace the original exception. Also, notice that you can localize your policy by specifying the name and type of the resource containing the localized message string.

new ReplaceHandler(
  "Application error will be ignored and processing will continue.",
  typeof(Exception))

Choosing an Exception Handling Strategy

So let's get back to our quiz question, "How should I handle exceptions?" You should be able to see from the options available for exception handling policies how you can implement the common strategies for handling exceptions:

  • Replace the exception with a different one and throw the new exception. This is an implementation of the Exception Shielding pattern. In your exception handling code, you can clean up resources or perform any other relevant processing. You use a Replace handler in your exception handling policy to replace the exception with a different exception containing sanitized or new information that does not reveal sensitive details about the source of the error, the application, or the operating system. Add a Logging handler to the exception policy if you want to log the exception. Place it before the Replace handler to log the original exception, or after it to log the replacement exception (if you log sensitive information, make sure your log files are properly secured). Set the post-handling action to ThrowNewException so that the block will throw the new exception.
    Dn440728.note(en-us,PandP.60).gifPoe says:
    Poe If you are logging exceptions that contain sensitive information either sanitize the data before it is written to the log, or ensure that the log is properly secured.
    - **Wrap the exception to preserve the content and then throw the new exception**. This is an implementation of the Exception Translation pattern. In your exception handling code, you can clean up resources or perform any other relevant processing. You use a **Wrap** handler in your exception-handling policy to wrap the exception within another exception and then throw the new exception so that code higher in the code stack can handle it. This approach is useful when you want to keep the original exception and its information intact, and/or provide additional information to the code that will handle the exception. Add a **Logging** handler to the exception policy if you want to log the exception. Place it before the **Wrap** handler to log the original exception, or after it to log the enclosing exception. Set the post-handling action to **ThrowNewException** so that the block will throw the new exception. - **Log and, optionally, re-throw the original exception**. In your exception handling code, you can clean up resources or perform any other relevant processing. You use a **Logging** handler in your exception handling policy to write details of the exception to the configured logging store such as Windows Event Log or a file (an implementation of the Exception Logging pattern). If the exception does not require any further action by code elsewhere in the application (for example, if a retry action succeeds), set the post-handling action to **None**. Otherwise, set the post-handling action to **NotifyRethrow**.****Your exception handling code can then decide whether to throw the exception. Alternatively, you can set it to **ThrowNewException** if you always want the Exception Handling block to throw the exception for you.

    Remember that the whole idea of using the Exception Handling block is to implement a strategy made up of configurable policies that you can change without having to edit, recompile, and redeploy the application. For example, the block allows you (or an administrator) to:

    • Add, remove, and change the types of handlers (such as the Wrap, Replace, and Logging handlers) that you use for each exception policy, and change the order in which they execute.
    • Add, remove, and change the exception types that each policy will handle, and the types of exceptions used to wrap or replace the original exceptions.
    • Modify the target and style of logging, including modifying the log messages, for each type of exception you decide to log. This is useful, for example, when testing and debugging applications.
    • Decide what to do after the block handles the exception. Provided that the exception handling code you write checks the return value from the call to the Exception Handling block, the post-handling action will allow you or an administrator to specify whether the exception should be thrown. Again, this is extremely useful when testing and debugging applications.

    Process or HandleException?

    The Exception Handling block provides two ways for you to manage exceptions. You can use the Process method to execute any method in your application, and have the block automatically perform management and throwing of the exception. Alternatively, if you want to apply more granular control over the process, you can use the HandleException method. The following will help you to understand which approach to choose.

    • The Process method is the most common approach, and is useful in the majority of cases. You specify either a delegate or a lambda expression that you want to execute. The Exception Handling block executes the method represented by the delegate or the body of the lambda expression, and automatically manages any exception that occurs. You will generally specify a PostHandlingAction of ThrowNewException so that the block automatically throws the exception that results from executing the exception handling policy. However, if you want the code to continue to execute (instead of throwing the exception), you can set the PostHandlingAction of your exception handling policy to None.
    • The HandleException method is useful if you want to be able to detect the result of executing the exception handling policy. For example, if you set the PostHandlingAction of a policy to NotifyRethrow, you can use the return value of the HandleException method to determine whether or not to throw the exception. You can also use the HandleException method to pass an exception to the block and have it return the exception that results from executing the policy—which might be the original exception, a replacement exception, or the original exception wrapped inside a new exception.

    You will see both the Process and the HandleException techniques described in the following examples, although most of them use the Process method.

    Using the Process Method

    The Process method has several overloads that make it easy to execute functions that return a value, and methods that do not. Typically, you will use the Process method in one of the following ways:

    • To execute a routine or method that does not accept parameters and does not return a value:

      exManager.Process(method_name, "Exception Policy Name");
      
    • To execute a routine that does accept parameters but does not return a value:

      exManager.Process(() => method_name(param1, param2), "Exception Policy Name");
      
    • To execute a routine that accepts parameters and returns a value:

      var result = exManager.Process(() => method_name(param1, param2), 
                                     "Exception Policy Name");
      
    • To execute a routine that accepts parameters and returns a value, and to also supply a default value to be returned should an exception occur and the policy that executes does not throw the exception. If you do not specify a default value and the PostHandlingAction is set to None, the Process method will return null for reference types, zero for numeric types, or the default empty value for other types should an exception occur.

      var result = exManager.Process(() => method-name(param1, param2), 
                                     default_result_value,
                                     "Exception Policy Name");
      
    • To execute code defined within the lambda expression itself:

      exManager.Process(() =>
        {
          // Code lines here to execute application feature    // that may raise an exception that the Exception    // Handling block will handle using the policy named    // in the final parameter of the Process method.    // If required, the lambda expression defined here     // can return a value that the Process method will    // return to the calling code.
        },
        "Exception Policy Name");
      

    Note

    The Process method is optimized for use with lambda expressions, which are supported in C# 3.0 on version 3.5 of the .NET Framework and in Visual Studio 2008 onwards. If you are not familiar with lambda functions or their syntax, see the "Lambda Expressions" article on MSDN. For a full explanation of using the HandleException method, see the "Key Scenarios" topic in the Enterprise Library Reference Documentation.

    Using the HandleException Method

    You can use the HandleException method to determine whether an exception handling policy recommends that your code should rethrow an exception. There are two overloads of this method.

    Dn440728.note(en-us,PandP.60).gifMarkus says:
    Markus When you use the HandleException method, you are responsible for invoking the code that may throw an exception and for catching the exception.

    The section "Executing Code around Exception Handling" later in this chapter shows how you can use the HandleException to add additional logic around the exception management provided by the block.

    The following code sample illustrates the usage of the first overload.

    try
    {
      SalaryCalculator calc = new SalaryCalculator();
      Console.WriteLine("Result is: {0}", 
        calc.RaiseArgumentOutOfRangeException("jsmith", 0));
    }
    catch (Exception e)
    {
      if (exceptionManager.HandleException(e, "Exception Policy Name")) throw;
    }
    

    The following code sample illustrates the usage of the second overload. In this scenario, the HandleException method specifies an alternative exception to throw.

    try
    {
      SalaryCalculator calc = new SalaryCalculator();
      Console.WriteLine("Result is: {0}", 
        calc.RaiseArgumentOutOfRangeException("jsmith", 0));
    }
    catch (Exception e)
    {
      Exception exceptionToThrow;
      if (exceptionManager.HandleException(e, "Exception Policy Name",
        out exceptionToThrow))
      {
        if(exceptionToThrow == null)
          throw;
        else
          throw exceptionToThrow;
      }
    }
    

    Diving in with a Simple Example

    The code you can download for this guide contains a sample application named ExceptionHandling that demonstrates the techniques described in this chapter. The sample provides a number of different examples that you can run. They illustrate each stage of the process of applying exception handling described in this chapter. However, this chapter describes an iterative process of updating a single application scenario. To make it easier to see the results of each stage, we have provided separate examples for each of them.

    Note

    If you run the examples under the Visual Studio debugger, you will find that the code halts when an exception occurs—before it is sent to the Exception Handling block. You can press F5 at this point to continue execution. Alternatively, you can run the examples by pressing Ctrl-F5 (non-debugging mode) to prevent this from happening.

    To see how you can apply exception handling strategies and configure exception handling policies, we'll start with a simple example that causes an exception when it executes. First, we need a class that contains a method that we can call from our main routine, such as the following in the SalaryCalculator class of the example application.

    public Decimal GetWeeklySalary(string employeeId, int weeks)
    {
      String connString = string.Empty;
      String employeeName = String.Empty;
      Decimal salary = 0;
      try
      {
        connString = ConfigurationManager.ConnectionStrings
                                          ["EmployeeDatabase"].ConnectionString;
        // Access database to get salary for employee here...
        // In this example, just assume it's some large number.
        employeeName = "John Smith";
        salary = 1000000;
        return salary / weeks;
      }
      catch (Exception ex)
      {
        // provide error information for debugging
        string template = "Error calculating salary for {0}." 
                        + " Salary: {1}. Weeks: {2}\n" 
                        + "Data connection: {3}\n{4}";
        Exception informationException = new Exception(
            string.Format(template, employeeName, salary, weeks, 
                          connString, ex.Message));
        throw informationException;
      }
    }
    

    You can see that a call to the GetWeeklySalary method will cause an exception of type DivideByZeroException when called with a value of zero for the number of weeks parameter. The exception message contains the values of the variables used in the calculation, and other information useful to administrators when debugging the application. Unfortunately, the current code has several issues. It trashes the original exception and loses the stack trace, preventing meaningful debugging. Even worse, the global exception handler for the application presents any user of the application with all of the sensitive information when an error occurs.

    Dn440728.note(en-us,PandP.60).gifMarkus says:
    Markus The application should also raise a more specific type of exception such as SalaryCalculationException rather than using the Exception type. For more information on , see the "Design Guidelines for Exceptions" on MSDN.

    If you run the example for this chapter, and select option Typical Default Behavior without Exception Shielding, you will see this result generated by the code in the catch statement:

    Exception type ExceptionHandlingExample.SalaryException was thrown.
    Message: 'Error calculating salary for John Smith. 
    Salary: 1000000. Weeks: 0 
    Connection: Database=Employees;Server=CorpHQ;
    User ID=admin;Password=2g$tXD76qr Attempted to divide by zero.'
    Source: 'ExceptionHandlingExample'
    No inner exception
    

    Applying Exception Shielding

    It's clear that the application as it stands has a severe security hole that allows it to reveal sensitive information. Of course, we could prevent this by not adding the sensitive information to the exception message. However, the information will be useful to administrators and developers if they need to debug the application. For example, if the data connection had failed or the database contained invalid data, they would have seen this through missing values for the employee name or salary; and they could see if the configuration file contains the correct database connection string. Alternatively, in the case shown here, they can immediately tell that the database returned the required values for the operation, but the user interface allowed the user to enter the value zero for the number of weeks.

    To provide this extra information, yet apply exception shielding, you may consider implementing configuration settings and custom code to allow administrators to specify when they need the additional information. However, this is exactly where the Exception Handling block comes in. You can set up an exception handling policy that administrators can modify as required, without needing to write custom code or set up custom configuration settings.

    Dn440728.note(en-us,PandP.60).gifBeth says:
    Beth You can create a policy that enables an administrator to control through the configuration file how much information is included in the exceptions raised by the application.

    The first step is to create an exception handling policy that specifies the events you want to handle, and contains a handler that will either wrap or replace the exception containing all of the debugging information with one that contains a simple error message suitable for display to users or propagation through the layers of the application. You'll see these options implemented in the following sections. You will also see how you can log the original exception before replacing it, how you can handle specific types of exceptions, and how you can apply exception shielding to WCF services.

    Wrapping an Exception

    If you want to retain the original exception and the information it contains, you can wrap the exception in another exception and specify a sanitized user-friendly error message for the containing exception. This error message is available for the global error handler to display. However, code elsewhere in the application (such as code in a calling layer that needs to access and log the exception details) can access the contained exception and retrieve the information it requires before passing the exception on to another layer or to the global exception handler.

    Configuring the Wrap Handler Policy

    So, the first stage is to configure the exception handling policy you require. You need to add a policy that specifies the type of exception returned from your code (in this case, we'll specify the base class Exception), and set the PostHandlingAction property for this exception type to ThrowNewException so that the Exception Handling block will automatically throw the new exception that wraps the original exception. Then, add a Wrap handler to the policy, and specify the exception message and the type of exception you want to use to wrap the original exception (we chose Exception here again). The following code sample shows the completed configuration.

    var exceptionShielding = new List<ExceptionPolicyEntry>
      {
          {
              new ExceptionPolicyEntry(typeof (Exception),
                  PostHandlingAction.ThrowNewException,
                  new IExceptionHandler[]
                    {
                      new WrapHandler(
                        "Application Error. Please contact your administrator.",
                        typeof(Exception))
                    })
          }
      };
    

    Initializing the Exception Handling Block

    You must edit your code to use the Exception Handling block. You'll need to add references to the appropriate Enterprise Library assemblies and namespaces. The examples in this chapter demonstrate logging exception information and handling exceptions in a WCF application, as well as the basic processes of wrapping and replacing exceptions, so we'll add references to all of the assemblies and namespaces required for these tasks.

    You can use NuGet to add the Exception Handling Application Block to your project. There are three NuGet packages:

    • Enterprise Library - Exception Handling Application Block
    • Enterprise Library - Exception Handling Application Block Logging Handler
    • Enterprise Library - Exception Handling Application Block WCF Provider

    Note

    If you are only wrapping and replacing exceptions in your application but not logging them, you don't need to install the Logging Handler package. If you are not using the block to shield WCF services, you don't need to install the WCF Provider package.

    To make it easier to use the objects in the Exception Handling block, you can add references to the relevant namespaces to your project.

    Now you can obtain an instance of the ExceptionManager class you'll use to perform exception management. The section "Instantiating Enterprise Library Objects" in Chapter 1, "Introduction" describes the different approaches you can use. You can choose to define the configuration information programmatically or load it from a configuration file. The examples you can download for this chapter use the programmatic approach: see the BuildExceptionManagerConfig method in the sample application. If you want to load the configuration data from a configuration file, you can do this by calling the CreateManager method of the ExceptionPolicyFactory class, as shown here, and storing the instance in application-wide variable so that it can be accessed from anywhere in the code.

    // Create the default ExceptionManager object 
    // from the configuration settings.
    ExceptionPolicyFactory policyFactory = new ExceptionPolicyFactory();
    exManager = policyFactory.CreateManager();
    

    Editing the Application Code to Use the New Policy

    Now you can update your exception handling code to use the new policy. You have two choices. If the configured exception policy does everything you need to do, you can actually remove the try...catch block completely from the method that may cause an error, and—instead—use the Process method of the ExceptionManager to execute the code within the method, as shown here.

    public Decimal GetWeeklySalary(string employeeId, int weeks)
    {
      string employeeName = String.Empty;
      Decimal salary = 0;
      Decimal weeklySalary = 0;
      
      exManager.Process(() => {
        string connString = ConfigurationManager.ConnectionStrings
                                          ["EmployeeDatabase"].ConnectionString;
        // Access database to get salary for employee.
        // In this example, just assume it's some large number.
        employeeName = "John Smith";
        salary = 1000000;
        weeklySalary = salary / weeks;
        },    "ExceptionShielding");
      
      return weeklySalary;
    }
    

    The body of your logic is placed inside a lambda function and passed to the Process method. If an exception occurs during the execution of the expression, it is caught and handled according to the configured policy. The name of the policy to execute is specified in the second parameter of the Process method.

    Alternatively, you can use the Process method in your main code to call the method of your class. This is a useful approach if you want to perform exception shielding at the boundary of other classes or objects. If you do not need to return a value from the function or routine you execute, you can create any instance you need and work with it inside the lambda expression, as shown here.

    exManager.Process(() => 
      {
        SalaryCalculator calc = new SalaryCalculator();
        Console.WriteLine("Result is: {0}", calc.GetWeeklySalary("jsmith", 0));
      }, 
      "ExceptionShielding");
    

    If you want to be able to return a value from the method or routine, you can use the overload of the Process method that returns the lambda expression value, like this.

    SalaryCalculator calc = new SalaryCalculator();
    var result = exManager.Process(() => 
                   calc.GetWeeklySalary("jsmith", 0), "ExceptionShielding");
    Console.WriteLine("Result is: {0}", result);
    

    Notice that this approach creates the instance of the SalaryCalculator class outside of the Process method, and therefore it will not pass any exception that occurs in the constructor of that class to the exception handling policy. But when any other error occurs, the global application exception handler sees the wrapped exception instead of the original informational exception. If you run the example Behavior After Applying Exception Shielding with a Wrap Handler, the catch section now displays the following. You can see that the original exception is hidden in the Inner Exception, and the exception that wraps it contains the generic error message.

    Exception type ExceptionHandlingExample.SalaryException was thrown.
    Message: 'Application Error. Please contact your administrator.'
    Source: 'Microsoft.Practices.EnterpriseLibrary.ExceptionHandling'
    
    Inner Exception: System.Exception: Error calculating salary for John Smith. 
    Salary: 1000000. Weeks: 0
    Connection: Database=Employees;Server=CorpHQ;User ID=admin;Password=2g$tXD76qr
    Attempted to divide by zero.
       at ExceptionHandlingExample.SalaryCalculator.GetWeeklySalary(String employeeI
    d, Int32 weeks) in ...\ExceptionHandling\ExceptionHandling\SalaryCalculator.cs:
    line 34
       at ExceptionHandlingExample.Program.<WithWrapExceptionShielding>b__0() in ...\ExceptionHandling\ExceptionHandling\Program.cs:line 109
       at Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionManagerIm
    pl.Process(Action action, String policyName)
    

    This means that developers and administrators can examine the wrapped (inner) exception to get more information. However, bear in mind that the sensitive information is still available in the exception, which could lead to an information leak if the exception propagates beyond your secure perimeter. While this approach may be suitable for highly technical, specific errors, for complete security and exception shielding, you should use the technique shown in the next section to replace the exception with one that does not contain any sensitive information.

    Dn440728.note(en-us,PandP.60).gifCarlos says:
    Carlos For simplicity, this example shows the principles of exception shielding at the level of the UI view. The business functionality it uses may be in the same layer, in a separate business layer, or even on a separate physical tier. Remember that you should design and implement an exception handling strategy for individual layers or tiers in order to shield exceptions on the layer or service boundaries.

    Replacing an Exception

    Having seen how easy it is to use exception handling policies, we'll now look at how you can implement exception shielding by replacing an exception with a different exception. To configure this scenario, simply create a policy in the same way as the previous example, but with a Replace handler instead of a Wrap handler, as shown in the following code sample.

    var replacingException = new List<ExceptionPolicyEntry>
      {
          {
              new ExceptionPolicyEntry(typeof (Exception),
                  PostHandlingAction.ThrowNewException,
                  new IExceptionHandler[]
                    {
                      new ReplaceHandler(
                        "Application Error. Please contact your administrator.",
                        typeof(SalaryException))
                    })
          }
      };
    

    When you call the method that generates an exception, you see the same generic exception message as in the previous example. However, there is no inner exception this time. If you run the example Behavior After Applying Exception Shielding with a Replace Handler, the Exception Handling block replaces the original exception with the new one specified in the exception handling policy. This is the result:

    Exception type ExceptionHandlingExample.SalaryException was thrown.
    Message: 'Application Error. Please contact your administrator.'
    Source: 'Microsoft.Practices.EnterpriseLibrary.ExceptionHandling'
    No Inner Exception
    

    Logging an Exception

    The previous section shows how you can perform exception shielding by replacing an exception with a new sanitized version. However, you now lose all the valuable debugging and testing information that was available in the original exception. You can preserve this information by chaining exception handlers within your exception handling policy. In other words, you add a Logging handler to the policy.

    Dn440728.note(en-us,PandP.60).gifJana says:
    Jana Logging details of the original exception enables you to capture all the valuable debugging and testing information that would otherwise be lost when you replace an exception with a new sanitized version.

    That doesn’t mean that the Logging handler is only useful as part of a chain of handlers. If you only want to log details of an exception (and then throw it or ignore it, depending on the requirements of the application), you can define a policy that contains just a Logging handler. However, in most cases, you will use a Logging handler with other handlers that wrap or replace exceptions.

    The following code sample shows what happens when you add a Logging handler to your exception handling policy. You must create a LogWriter instance to use with the handler. You also need to set a few properties of the Logging exception handler in the Exception Handling Settings section:

    • Specify the ID for the log event your code will generate as the Event ID property.
    • Specify the TextExceptionFormatter as the type of formatter the Exception Handling block will use. Click the ellipsis (...) button in the Formatter Type property and select TextExceptionFormatter in the type selector dialog that appears.
    • Set the category for the log event. The Logging block contains a default category named General, and this is the default for the Logging exception handler. However, if you configure other categories for the Logging block, you can select one of these from the drop-down list that is available when you click on the Logging Category property of the Logging handler.
    var loggingAndReplacing = new List<ExceptionPolicyEntry>
      {
          {
              new ExceptionPolicyEntry(typeof (Exception),
                  PostHandlingAction.ThrowNewException,
                  new IExceptionHandler[]
                    {
                      new LoggingExceptionHandler(
                        "General", 9000, TraceEventType.Error,
                        "Salary Calculations Service", 5,
                        typeof(TextExceptionFormatter), logWriter),
                      new ReplaceHandler(
                       "An application error occurred and has been logged.
                        Please contact your administrator.",
                        typeof(SalaryException))
                    })
          }
      };
    

    The following code sample shows the definition of the LogWriter instance.

    // Formatters
    TextFormatter formatter = new TextFormatter("Timestamp: ... ");
     
    // Listeners
    var flatFileTraceListener = new FlatFileTraceListener(..., formatter);
    var eventLog = new EventLog("Application", ".", "Enterprise Library Logging");
    var eventLogTraceListener = new FormattedEventLogTraceListener(eventLog);
    // Build Configuration
    var config = new LoggingConfiguration();
    config.AddLogSource("General", SourceLevels.All, true)
      .AddTraceListener(eventLogTraceListener);
    config.LogSources["General"].AddTraceListener(flatFileTraceListener);
     
    // Special Sources Configuration
    config.SpecialSources.LoggingErrorsAndWarnings.AddTraceListener(eventLogTraceListener);
     
    LogWriter logWriter = new LogWriter(config);
    

    In addition, if you did not already do so, you must add a reference to the Logging Application block assembly to your project and (optionally) add a using statement to your class, as shown here.

    using Microsoft.Practices.EnterpriseLibrary.Logging;
    

    Now, when the application causes an exception, the global exception handler continues to display the same sanitized error message. However, the Logging handler captures details of the original exception before the Exception Handling block policy replaces it, and writes the details to whichever logging sink you specify in the configuration for the Logging block. If you run the example Logging an Exception to Preserve the Information it Contains, you will see an exception like the one in Figure 1.

    Figure 1 - Details of the logged exception

    Follow link to expand image

    This example shows the Exception Handling block using the default settings for the Logging block. However, as you can see in Chapter 5, "As Easy As Falling Off a Log," the Logging block is extremely configurable. So you can arrange for the Logging handler in your exception handling policy to write the information to any Windows Event Log, an email message, a database, a message queue, a text file, or a custom location using classes you create that take advantage of the application block extension points.

    Shielding Exceptions at WCF Service Boundaries

    You can use the Exception Handling block to implement exception handling policies for WCF services. A common scenario is to implement the Exception Shielding pattern at a WCF service boundary. The Exception Handling block contains a handler specifically designed for this (the Fault Contract exception handler), which maps the values in the exception to a new instance of a fault contract that you specify.

    Creating a Fault Contract

    A fault contract for a WCF service will generally contain just the most useful properties for an exception, and exclude sensitive information such as the stack trace and anything else that may provide attackers with useful information about the internal workings of the service. The following code shows a simple example of a fault contract suitable for use with the Fault Contract exception handler:

    [DataContract]
    public class SalaryCalculationFault
    {
      [DataMember]
      public Guid FaultID { get; set; }
    
      [DataMember]
      public string FaultMessage { get; set; }
    }
    

    Configuring the Exception Handling Policy

    The following code sample shows a sample configuration for the Fault Contract exception handler. This specifies the type SalaryCalculationFault as the target fault contract type, and the exception message that the policy will generate to send to the client. Note that, when using the Fault Contract exception handler, you should always set the PostHandlingAction property to ThrowNewException so that the Exception Handling block throws an exception that forces WCF to return the fault contract to the client.

    var mappings = new NameValueCollection();
    mappings.Add("FaultID", "{Guid}");
    mappings.Add("FaultMessage", "{Message}");
     
    var salaryServicePolicy = new List<ExceptionPolicyEntry>
          {
                  new ExceptionPolicyEntry(typeof (Exception),
                      PostHandlingAction.ThrowNewException,
                      new IExceptionHandler[]
                        {
                          new FaultContractExceptionHandler(
                            typeof(SalaryCalculationFault), mappings)
                        })
          };
    

    Notice that we specified Property Mappings for the handler that map the Message property of the exception generated within the service to the FaultMessage property of the SalaryCalculationFault class, and map the unique Handling Instance ID of the exception (specified by setting the Source to "{Guid}") to the FaultID property.

    Editing the Service Code to Use the New Policy

    After you specify your fault contract and configure the Fault Contract exception handler, you must edit your service code to use the new exception policy. If you did not already do so, you must also add a reference to the assembly that contains the Fault Contract exception handler to your project and (optionally) add a using statement to your service class, as shown here:

    using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF;
    

    Now, you can add attributes to the methods in your service class to specify the policy they should use when an exception occurs, as shown in this code:

    [ServiceContract]
    public interface ISalaryService
    {
      [OperationContract]
      [FaultContract(typeof(SalaryCalculationFault))]
      decimal GetWeeklySalary(string employeeId, int weeks);
    }
     
    [DataContract]
    public class SalaryCalculationFault
    {
      [DataMember]
      public Guid FaultID { get; set; }
     
      [DataMember]
      public string FaultMessage { get; set; }
    }
     
    [ExceptionShielding("SalaryServicePolicy")]
    public class SalaryService : ISalaryService
    {
      public decimal GetWeeklySalary(string employeeId, int weeks)
      {
          
     
        SalaryCalculator calc = new SalaryCalculator();
        return calc.GetWeeklySalary(employeeId, weeks);
      }
    }
    

    You add the ExceptionShielding attribute to a service implementation class or to a service contract interface, and use it to specify the name of the exception policy to use. If you do not specify the name of a policy in the constructor parameter, or if the specified policy is not defined in the configuration, the Exception Handling block will automatically look for a policy named WCF Exception Shielding.

    Dn440728.note(en-us,PandP.60).gifJana says:
    Jana The ExceptionShielding attribute specifies the policy to use when the service is invoked using the specified contracts. Specifying the policy programmatically may be safer than using declarative configuration because a change to the declarative configuration might affect how the contract is fulfilled.

    You also need to invoke the static SetExceptionManager method on the ExceptionPolicy class to ensure that the policy is loaded. The following code sample (specific to ASP.NET hosting) shows a method invoked from the Application_Start method that does this:

    private void ConfigureExceptionHandlingBlock()
    {
      var policies = new List<ExceptionPolicyDefinition>();
     
      var mappings = new NameValueCollection();
      mappings.Add("FaultID", "{Guid}");
      mappings.Add("FaultMessage", "{Message}");
     
      var salaryServicePolicy = new List<ExceptionPolicyEntry>
        {
            new ExceptionPolicyEntry(typeof (Exception),
              PostHandlingAction.ThrowNewException,
              new IExceptionHandler[]
                {
                  new FaultContractExceptionHandler(
                    typeof(SalaryCalculationFault), mappings)
                })
        };
      policies.Add(new ExceptionPolicyDefinition(
        "SalaryServicePolicy", salaryServicePolicy));
    
      ExceptionManager exManager = new ExceptionManager(policies);
      ExceptionPolicy.SetExceptionManager(exManager);
    }
    

    The Fault Contract Exception Handler

    The Exception Handling block executes the Fault Contract exception handler that you specify in your policy when an exception occurs. Effectively, the Fault Contract handler is a specialized version of the Replace handler. It takes the original exception, generates an instance of the fault contract, populates it with values from the exception, and then throws a FaultException<YourFaultContractType> exception. The handler performs the following actions:

    • It generates a new instance of the fault contract class you specify for the FaultContractType property of the Fault Contract exception handler.
    • It extracts the values from the properties of the exception that you pass to the method.
    • It sets the values of the new fault contract instance to the values extracted from the original exception. It uses mappings between the exception property names and the names of the properties exposed by the fault contract to assign the exception values to the appropriate properties. If you do not specify a mapping, it matches the source and target properties that have the same name.

    The result is that, instead of a general service failure message, the client receives a fault message containing the appropriate information about the exception.

    The example Applying Exception Shielding at WCF Application Boundaries uses the service described above and the Exception Handling block WCF Fault Contract handler to demonstrate exception shielding. You can run this example in one of three ways:

    • Inside Visual Studio by starting it with F5 (debugging mode) and then pressing F5 again when the debugger halts at the exception in the SalaryCalculator class.
    • Inside Visual Studio by right-clicking SalaryService.svc in Solution Explorer and selecting View in Browser to start the service, then pressing Ctrl-F5 (non-debugging mode) to run the application.
    • By starting the SalaryService in Visual Studio (as described in the previous bullet) and then running the executable file ExceptionHandlingExample.exe in the bin\debug folder directly.

    The result is shown below. You can see that the exception raised by the SalaryCalculator class causes the service to return an instance of the SalaryCalculationFault type that contains the fault ID and fault message. However, the Exception Handling block captures this exception and replaces the sensitive information in the message with text that suggests the user contact their administrator. Research shows that users really appreciate this type of informative error message.

    Getting salary for 'jsmith' from WCF Salary Service...
    Exception type System.ServiceModel.FaultException`1[ExceptionHandlingExample.Sal
    aryService.SalaryCalculationFault] was thrown.
    Message: 'Service error. Please contact your administrator.'
    Source: 'mscorlib'
    No Inner Exception
    
    Fault contract detail:
    Fault ID: bafb7ec2-ed05-4036-b4d5-56d6af9046a5
    Message: Error calculating salary for John Smith. Salary: 1000000. Weeks: 0
    Connection: Database=Employees;Server=CorpHQ;User ID=admin;Password=2g$tXD76qr
    Attempted to divide by zero.
    

    You can also see, below the details of the exception, the contents of the original fault contract, which are obtained by casting the exception to the type FaultException<SalaryCalculationFault> and querying the properties. You can see that this contains the original exception message generated within the service. Look at the code in the example file, and run it, to see more details.

    Note

    It is not recommended to include the original message in the FaultException as this may expose the sensitive information you are trying to hide. This example uses the original message simply to illustrate how you can populate the FaultException.

    Handling Specific Exception Types

    So far, all of the examples have used an exception policy that handles a single exception type (in the examples, this is Exception so that the policy will apply to any type of exception passed to it). However, you can specify multiple exception types within a policy, and specify different handlers—including chains of handlers—for each exception type. The following code sample shows the configuration of a policy with two exception types defined for the policy named LogAndWrap (which is used in the next example). Each exception type has different exception handlers specified.

    var logAndWrap = new List<ExceptionPolicyEntry>
      {
          {
              new ExceptionPolicyEntry(typeof (DivideByZeroException),
                  PostHandlingAction.None,
                  new IExceptionHandler[]
                    {
                      new LoggingExceptionHandler("General", 9002,
                        TraceEventType.Error,
                        "Salary Calculations Service", 5,
                        typeof(TextExceptionFormatter), logWriter),
                      new ReplaceHandler("Application error will be ignored
                        and processing will continue.",
                        typeof(Exception))
                    })
          },
          {
              new ExceptionPolicyEntry(typeof (Exception),
                  PostHandlingAction.ThrowNewException,
                  new IExceptionHandler[]
                    {
                      new WrapHandler("An application error has occurred.",
                        typeof(Exception))
                    })
          }
     
      };
    

    The advantage of this capability should be obvious. You can create policies that will handle different types of exceptions in different ways and, for each exception type, can have different messages and post-handling actions as well as different handler combinations. They can add new exception types, modify the types specified, change the properties for each exception type and the associated handlers, and generally fine-tune the strategy to suit day-to-day operational requirements.

    Of course, this will only work if your application code throws the appropriate exception types. If you generate informational exceptions that are all of the base type Exception, as we did in earlier examples in this chapter, only the handlers for that exception type will execute.

    Dn440728.note(en-us,PandP.60).gifBeth says:
    Beth Of course, you should throw exceptions of specific types in in your application and not throw exceptions of the base type Exception.

    Executing Code around Exception Handling

    So far, all of the examples have used the Process method to execute the code that may cause an exception. They simply used the Process method to execute the target class method, as shown here.

    SalaryCalculator calc = new SalaryCalculator();
    var result = exManager.Process(() => 
                 calc.GetWeeklySalary("jsmith", 0), "ExceptionShielding");
    Console.WriteLine("Result is: {0}", result);
    

    However, as you saw earlier in this chapter, the Process method does not allow you to detect the return value from the exception handling policy executed by the Exception Handling block (it returns the value of the method or function it executes). In some cases, though perhaps rarely, you may want to detect the return value from the exception handling policy and perform some processing based on this value, and perhaps even capture the exception returned by the Exception Handling block to manipulate it or decide whether or not to throw it in your code.

    In this case, you can use the HandleException method to pass an exception to the block as an out parameter to be populated by the policy, and retrieve the Boolean result that indicates if the policy determined that the exception should be thrown or ignored.

    The example Executing Custom Code Before and After Handling an Exception, demonstrates this approach. The SalaryCalculator class contains two methods in addition to the GetWeeklySalary method we’ve used so far in this chapter. These two methods, named RaiseDivideByZeroException and RaiseArgumentOutOfRangeException, will cause an exception of the type indicated by the method name when called.

    The sample first attempts to execute the RaiseDivideByZeroException method, like this.

    SalaryCalculator calc = new SalaryCalculator();
    Console.WriteLine("Result is: {0}", calc.RaiseDivideByZeroException("jsmith", 0));
    

    This exception is caught in the main routine using the exception handling code shown below. This creates a new Exception instance and passes it to the Exception Handling block as the out parameter, specifying that the block should use the NotifyingRethrow policy. This policy specifies that the block should log DivideByZero exceptions, and replace the message with a sanitized one. However, it also has the PostHandlingAction set to None, which means that the HandleException method will return false. The sample code simply displays a message and continues.

    ...
    catch (Exception ex)
    {
      Exception newException;
      bool rethrow = exManager.HandleException(ex, "NotifyingRethrow", 
                                               out newException);
      if (rethrow)
      {
        // Exception policy could specify to throw the existing exception
        // or the new exception.
        // Code here to perform any clean up tasks required.
        if (newException == null)
          throw;
        else
          throw newException;
      }
      else
      {
        // Exception policy setting is "None" so exception is not thrown.
        // Code here to perform any other processing required.
        // In this example, just ignore the exception and do nothing.
        Console.WriteLine("Detected and ignored Divide By Zero Error "
                        + "- no value returned.");
      }
    }
    

    Therefore, when you execute this sample, the following message is displayed.

    Getting salary for 'jsmith' ... this will raise a DivideByZero exception.
    Detected and ignored Divide By Zero Error - no value returned.
    

    The sample then continues by executing the RaiseArgumentOutOfRangeException method of the SalaryCalculator class, like this.

    SalaryCalculator calc = new SalaryCalculator();
    Console.WriteLine("Result is: {0}",
                       calc.RaiseArgumentOutOfRangeException("jsmith", 0));
    

    This section of the sample also contains a catch section, which is—other than the message displayed to the screen—identical to that shown earlier. However, the NotifyingRethrow policy specifies that exceptions of type Exception (or any exceptions that are not of type DivideByZeroException) should simply be wrapped in a new exception that has a sanitized error message. The PostHandlingAction for the Exception type is set to ThrowNewException, which means that the HandleException method will return true. Therefore the code in the catch block will throw the exception returned from the block, resulting in the output shown here.

    Getting salary for 'jsmith' ... this will raise an ArgumentOutOfRange exception.
    
    Exception type System.Exception was thrown.
    Message: 'An application error has occurred.'
    Source: 'ExceptionHandlingExample'
    
    Inner Exception: System.ArgumentOutOfRangeException: startIndex cannot be larger
     than length of string.
    Parameter name: startIndex
       at System.String.InternalSubStringWithChecks(Int32 startIndex, Int32 length,
    Boolean fAlwaysCopy)
       at System.String.Substring(Int32 startIndex, Int32 length)
       at ExceptionHandlingExample.SalaryCalculator.RaiseArgumentOutOfRangeException
    (String employeeId, Int32 weeks) in ...\ExceptionHandling\ExceptionHandling\Sala
    ryCalculator.cs:line 57
       at ExceptionHandlingExample.Program.ExecutingCodeAroundException(Int32 positi
    onInTitleArray) in ...\ExceptionHandling\ExceptionHandling\Program.cs:line 222
    

    You can also use the static HandleException method of the ExceptionPolicy class to determine what action you should take when you handle an exception. Before you use this static method you must set the exception manager as shown in the following code sample:

    ExceptionManager exManager;
    ...
    ExceptionPolicy.SetExceptionManager(exManager);
    

    The following code from the example Using the static ExceptionPolicy class demonstrates this approach.

    try
    {
      result = calc.GetWeeklySalary("jsmith", 0);
    }
    catch (Exception ex)
    {
      Exception exceptionToThrow;
      if (ExceptionPolicy.HandleException(ex, "ExceptionShielding",
          out exceptionToThrow))
      {
        if (exceptionToThrow == null)
          throw;
        else
          throw exceptionToThrow;
      }
    }
    

    Assisting Administrators

    Some would say that the Exception Handling block already does plenty to make an administrator's life easy. However, it also contains features that allow you to exert extra control over the way that exception information is made available, and the way that it can be used by administrators and operations staff. If you have ever worked in a technical support role, you'll recognize the scenario. A user calls to tell you that an error has occurred in the application. If you are lucky, the user will be able to tell you exactly what they were doing at the time, and the exact text of the error message. More likely, he or she will tell you that they weren't really doing anything, and that the message said something about contacting the administrator.

    Dn440728.note(en-us,PandP.60).gifPoe says:
    Poe You can’t rely on users to report accurately all of the details of an exception. The HandlingInstanceID value enables you to give the end user a single piece of information that you can use to identify the specific circumstances of an exception in the application. In addition, you should consider using end-to-end tracing to correlate all of the information about a single user action.

    To resolve this regularly occurring problem, you can make use of the HandlingInstanceID value generated by the block to associate logged exception details with specific exceptions, and with related exceptions. The Exception Handling block creates a unique GUID value for the HandlingInstanceID of every execution of a policy. The value is available to all of the handlers in the policy while that policy is executing. The Logging handler automatically writes the HandlingInstanceID value into every log message it creates. The Wrap and Replace handlers can access the HandlingInstanceID value and include it in a message using the special token {handlingInstanceID}.

    The following code sample shows how you can configure a Logging handler and a Replace handler in a policy, and include the {handlingInstanceID} token in the Exception Message property of the Replace handler.

    var assistingAdministrators = new List<ExceptionPolicyEntry>
      {
          {
              new ExceptionPolicyEntry(typeof (Exception),
                  PostHandlingAction.ThrowNewException,
                  new IExceptionHandler[]
                    {
                      new LoggingExceptionHandler("General", 9001,
                        TraceEventType.Error,
                        "Salary Calculations Service", 5,
                        typeof(TextExceptionFormatter), logWriter),
                      new ReplaceHandler("Application error.
                        Please advise your administrator and provide
                        them with this error code: {handlingInstanceID}",
                        typeof(SalaryException))
                    })
          }
      };
    

    Now your application can display the unique exception identifier to the user, and they can pass it to the administrator who can use it to identify the matching logged exception information. This logged information will include the information from the original exception, before the Replace handler replaced it with the sanitized exception. If you select the option Providing Assistance to Administrators for Locating Exception Details in the example application, you can see this in operation. The example displays the following details of the exception returned from the exception handling policy:

    Exception type ExceptionHandlingExample.SalaryException was thrown.
    Message: 'Application error. Please advise your administrator and provide them
    with this error code: 22f759d3-8f58-43dc-9adc-93b953a4f733'
    Source: 'Microsoft.Practices.EnterpriseLibrary.ExceptionHandling'
    No Inner Exception
    

    In a production application, you will probably show this message in a dialog of some type. One issue, however, is that users may not copy the GUID correctly from a standard error dialog (such as a message box). If you decide to use the HandlingInstanceID value to assist administrators, consider using a form containing a read-only text box or an error page in a web application to display the GUID value in a way that allows users to copy it to the clipboard and paste into a document or email message.

    Extending Your Exception Handling

    Like all of the Enterprise Library application blocks, the Exception Handling block is extensible. You can create new exception handlers and exception formatters if you need to perform specific tasks that the block does not implement by default. For example, you could create an exception handler that displays the exception details in a dialog, creates an XML message, or generates a web page. All that is required is that you implement the IExceptionHandler interface defined within the block. This interface contains one method named HandleException that the Exception Handling block will execute when you add your handler to a policy chain.

    For information about adding support for declarative configuration in custom handlers, see the topic Extending and Modifying the Exception Handling Application Block in the Enterprise Library Reference Documentation.

    The Exception Handling block uses formatters to create the message sent to the Logging block when you use a Logging handler. The two formatters provided are the TextExceptionFormatter and the XmlExceptionFormatter. The TextExceptionFormatter generates a simple text format for the error message, as you have seen in the previous sections of this chapter. The XmlExceptionFormatter generates, as you would expect, an XML-formatted document as the error message. You can create custom formatters if you want to control the exact format of this information. You simply create a new class that derives from the ExceptionFormatter base class in the Exception Handling block, and override the several methods it contains for formatting the exception information as required.

    Summary

    In this chapter you have seen why, when, and how you can use the Enterprise Library Exception Handling block to create and implement exception handling strategies. Poor error handling can make your application difficult to manage and maintain, hard to debug and test, and may allow it to expose sensitive information that would be useful to attackers and malicious users.

    A good practice for exception management is to implement strategies that provide a controlled and decoupled approach to exception handling through configurable policies. The Exception Handling block makes it easy to implement such strategies for your applications, irrespective of their type and complexity. You can use the Exception Handling block in Web and Windows Forms applications, Web services, console-based applications and utilities, and even in administration scripts and applications hosted in environments such as SharePoint, Microsoft Office applications, or other enterprise systems.

    This chapter demonstrated how you can implement common exception handling patterns, such as Exception Shielding, using techniques such as wrapping, replacing, and logging exceptions. It also demonstrated how you can handle different types of exceptions, assist administrators by using unique exception identifiers, and extend the Exception Handling block to perform tasks that are specific to your own requirements.

    More Information

    All links in this book are accessible from the book's online bibliography on MSDN at https://aka.ms/el6biblio.

    For a more detailed discussion of whether you should catch an exception at a particular level in your application or allow it to propagate up through the call stack, see Eric Lippert’s "Vexing exceptions" blog post and Raymond Chen’s "Cleaner, more elegant, and harder to recognize" blog post.

    If you are not familiar with lambda functions or their syntax, see the "Lambda Expressions" article on MSDN.

    For a full explanation of using the HandleException method, see the "Key Scenarios" topic in the Enterprise Library Reference Documentation.

    For more information on raising specific types of exceptions, see the "Design Guidelines for Exceptions" on MSDN.

    For information about adding support for declarative configuration in custom handlers, see the topic Extending and Modifying the Exception Handling Application Block in the Enterprise Library Reference Documentation.

    General Links:

    Next Topic | Previous Topic | Home | Community