To err is human, and we developers are but humans. It's a myth that we can develop bug-free software at one go. We can definitely take measures to reduce bugs through test-driven development, unit test cases, stringent code check-in policy, and so on. However, the fact is that there are bugs in every application and they will show their ugly faces in the production environment. Additionally, applications have to face unforeseen scenarios such as the database server not being available, network failure, and so on. Hence, handling exceptions and providing meaningful and user-friendly messages to the user helps in avoiding/reducing user frustration. We need to handle exceptions not only to gracefully recover but also to log useful information, which can be used to fix bugs in the application.
Many good developers or project teams develop reusable components to handle and manage exceptions within and across software projects. Unfortunately, developing a good reusable component that caters to various requirements involves huge cost and effort, and also, maintenance of such in-house components is a nightmare. The Exception Handling Application Block is a reusable library that addresses many common requirements that developers have to deal with and there is enough room for extensibility through custom implementation to satisfy unique requirements. The beauty of the application block lies in its design. We have to sprinkle very much less code in our application to manage exceptions. The configuration determines how an exception is processed and the application code dictates which policy processes the exception. This flexibility allows the application to modify the exception handling process without recompiling the code.
In this chapter, you will:
Before we leverage and dig deeper into individual features of the Exception Handling block, we will create a simple application that will help us to get up-to-speed with the basics. In this section, we will do the following:
To complement the concepts and sample code of this book and allow you to gain quick hands-on experience of different features of the Exception Handling Application Block, we have created a sample demonstration application, which simulates different layers of an application.
A screenshot of the sample application follows:
For the purposes of this demonstration, we will be referencing non-strong-named assemblies but, based on individual requirements, Microsoft strong-named assemblies or a modified set of custom assemblies can be referenced as well.
The following table lists the required/optional assemblies:
Assembly |
Required/Optional |
---|---|
|
Required |
|
Required |
|
Required |
|
Required |
|
Required |
|
Optional Used while leveraging Logging functionality |
|
Optional; used only if exception logging is configured to be stored in database |
Before we can leverage the features of the Exception Handling block, we have to add the initial Exception Handling Settings to the configuration. Open the Enterprise Library configuration editor either using the shortcut available in Start | All Programs | Microsoft patterns & practices | Enterprise Library 5.0 | Enterprise Library Configuration or just by right-clicking the configuration file in the Solution Explorer window of Visual Studio IDE and clicking on Edit Enterprise Library V5 Configuration. Initially, we will have a blank configuration file with default Application Settings and Database Settings.
The following screenshot displays the default settings displayed in the configuration editor:
Let us go ahead and add the Exception Handling Settings in the configuration file. Select the menu option Blocks, which lists many different settings to be added to the configuration, and click on the Add Exception Handling Settings menu item to add the configuration settings.
The following screenshot shows the menu option Add Exception Handling Settings:
Once we click on Add Exception Handling Settings the configuration editor will display the default Exception Handling Settings as shown in the following screenshot:
Notice that the settings consist of three sections: Policies, Exception Types, and Handlers. By default, a policy named Policy with exception type All Exceptions is added to the configuration. We will change the default configuration later, but for now, we are in good shape with regards to the initial infrastructure configuration.
Instead of fully qualifying the type on every instance of its usage, we can add the namespace given below to the source code file to use the Exception Handling block elements without fully qualifying the reference.
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling
Configuration Namespace (Optional): Required while using the EnterpriseLibraryContainer
to instantiate objects.
Microsoft.Practices.EnterpriseLibrary.Common.Configuration
Unity Namespace (Optional): Required while instantiating objects using UnityContainer
.
System.Configuration
Microsoft.Practices.Unity
Microsoft.Practices.Unity.Configuration
WCF Namespace (Optional): Required while leveraging the Exception Handling block in a WCF Service.
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF
The Exception Handling Application Block is driven by elements such as Exception Policy, Exception Types, Exception Handler and the ExceptionManager
class.
Exception policy is like creating a group under which one or more exception types are configured and under each exception type, one or more handlers can be configured. For example, we might have Data Access Exception Policy to handle data access-related exceptions with multiple exception types such as DBConcurrencyException, DbException
, and so on. While configuring the exception policy we have to provide a unique name, which can be used in the application code to process the exception.
The following class diagram shows the method exposed by the ExceptionPolicy
class:
Exception type is nothing but any type that inherits from System.Exception
; based on the configuration the application block chooses the matching exception type in the class hierarchy. If an exception of type System.NotFiniteNumberException
is thrown and the policy is configured to handle exceptions of types System.Exception
and System.ArithmeticException
then the application block will process the exception using System.ArithmeticException
based on the class hierarchy. While configuring the exception type, we have to decide on the post handling action. There are three options to choose from.
Exception handlers are .NET
classes that implement the Exception Handling block's interface called IExceptionHandler
. The application block includes the four commonly required handlers Wrap, Replace, Logging, and Fault Contract Exception Handler (used to guard the WCF service boundary and generate new fault contract from the exception). We can also implement custom handlers to meet our custom requirements and configure them in the configuration file using the editor.
The following class diagram shows various concrete implementations of exception handlers and the IExceptionHandler
interface:
The description of each of the concrete exception handlers is given as follows:
FaultContract
from the exception; developers working on the WCF service would appreciate the ability to shield the exception and return the configured FaultContract
based on the exception type as part of the response. IExceptionHandler
interface.
ExceptionManager
is one of the key classes of the Exception Handling Application Block; this abstract class is part of the Microsoft.Practices.EnterpriseLibrary.ExceptionHandling
namespace. It acts as an entry point to the exception handling functionality and provides two different ways to manage exceptions. The signatures of both the HandleException
and Process
methods are given next. The actual implementation is provided by the ExceptionManagerImpl
class, which inherits from the ExceptionManager
class.
The following class diagram shows the inheritance hierarchy and methods exposed by ExceptionManager
and the ExceptionManagerImpl
class:
The HandleException
method provides granular control while processing exceptions; it returns a Boolean
value indicating whether or not an exception re-throw is recommended. Typical usage of the HandleException
method will be similar to the code snippet given next:
try { BusinessLayer.BlogManager blogManager = new BusinessLayer.BlogManager(); //Get Blog Post BusinessLayer.BlogPost post = blogManager.GetBlogPost(0); } catch (ArgumentException ex) { Exception exceptionToRethrow; //Get instance of ExceptionManager using static method of Enterprise Library Container ExceptionManager exManager = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>(); //Call to HandleException method //Return value indicates whether to re-throw the exception bool rethrow = exManager.HandleException(ex, "General Policy", out exceptionToRethrow); if (rethrow) throw exceptionToRethrow; }
The Process
method automatically performs exception management and throws the exception based on the configuration. It accepts the policy name and a delegate or a lambda expression; the application block manages any exception that occurs while executing the method or lambda expression, also if the postHandlingAction
is set to ThrowNewException
then the application block throws the exception as a result of the respective execution of the configured exception handlers.
Typical usage of the Process
method will be similar to the code snippet given next:
//Get instance of ExceptionManager using static method of Enterprise Library Container ExceptionManager exManager = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>(); BusinessLayer.BlogPost post = null; BusinessLayer.BlogManager blogManager = new BusinessLayer.BlogManager(); //try..catch block not required... //Automatic Exception Management through Process method post = exManager.Process<BusinessLayer.BlogPost>( () => { return blogManager.GetBlogPost(0); }, "Data Access Policy" );
Let us stitch together the three fundamental elements (exception policy/type/handler) to put things in perspective and understand them better. Imagine that we want all the database-related exceptions to be replaced with a new exception to prevent disclosing the connection string information; additionally, we want to log the exception in a file to identify the root cause of the exception. To achieve this, we define a policy named Data Access Exception Policy. Now we can associate one or more data access-related exception types (SqlException, SqlTypeException, DBConcurrencyException
, and so on) and configure one or more exception handlers for each exception type. As we want to replace the exception and also log the exception information, we can configure the Logging handler first and then the Replace handler.
We have several options at hand while creating an Exception Handling object such as using the static ExceptionPolicy
class, using Unity service locator, and using Unity container directly. A few approaches such as configuring the container through a configuration file or code are not listed here but the recommended approach is either to use the Unity service locator for applications with few dependencies or create objects using Unity container directly to leverage the benefits of this approach. Use of the static factory class is not recommended.
ExceptionPolicy
is a static class and is part of the Microsoft.Practices.EnterpriseLibrary.ExceptionHandling
namespace. This class contains static methods to handle exceptions. Internally, it leverages EnterpriseLibraryContainer
, which is part of the Microsoft.Practices.EnterpriseLibrary.Common.Configuration
namespace. This class is an entry point for the container infrastructure for the Enterprise Library. The ExceptionPolicy
class was the default approach to handle exceptions in versions prior to 5.0. This approach is no longer recommended and is still available for backwards compatibility.
The following is the syntax to handle exceptions using the ExceptionPolicy
static class:
This approach is recommended for applications with few dependencies. The EnterpriseLibraryContainer
class exposes a static property called Current
of type IServiceLocator
, which resolves and gets an instance of the specified type.
The following is the syntax to create an instance of ExceptionManager
using Unity service locator:
//Get instance of ExceptionManager using static method of Enterprise Library Container ExceptionManager exManager = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>();
Larger complex applications demand looser coupling. This approach leverages the dependency injection mechanism to create objects instead of explicitly creating instances of concrete implementations. Unity container resolves objects using type registrations and mappings; these can be configured programmatically or through a configuration file. Based on the configuration, it resolves the appropriate type whenever requested. The following example instantiates a new Unity container object and adds the Enterprise Library Core Extension. This loads the configuration and makes registrations and mappings of Enterprise Library available.
The following is the syntax to create an instance of ExceptionManager
directly using Unity Container:
var container = new UnityContainer(); container.AddNewExtension<EnterpriseLibraryCoreExtension>(); ExceptionManager exManager = container.Resolve<ExceptionManager>();
3.144.113.55