Click here to Skip to main content
15,881,172 members
Articles / Programming Languages / C#

Hierarchical Exception Handling with the Enterprise Library

Rate me:
Please Sign up or sign in to vote.
2.60/5 (6 votes)
10 Oct 2007CDDL5 min read 41.4K   129   19   8
Defining exception policies in muliple locations.

Introduction

Recently, I encountered a situation where the solution was to have a common implementation for Exception Handling across all applications. Naturally, Microsoft's Enterprise Library and the Exception Handling Application Block came to mind. To satisfy the need for common handling across applications, the configuration files would need to be the same, or at least the Exception Handling entries would need to be. This could, of course, be handled by linking a common config file via source control, or simple cut & paste. The latter, though, would quickly degenerate into an unsupportable mess. The former isn't perfect either, and would be broken by branching the code base.

One way to handle this situation, while still making use of the Enterprise Library, is to have an assembly that encapsulates the common configuration settings, while still allowing for application level settings.

Exploring Exception Handling Policies

If you have used the Exception Handling Application Block, then you should be familiar with this piece of code:

C#
ExceptionPolicy.HandleException(exception, "Policy to Use");

What happens here is that the static class, ExceptionPolicy, will search the configuration file for the specified policy under the exceptionHandling/exceptionPolicies section. If found, it will check if the specified exception type has been defined, and will use any handlers that have been defined to process the exception.

XML
<exceptionHandling>
   <exceptionPolicies>
      <add name="Policy to Use">
         <exceptionTypes>
            <add type="System.Exception, ...>
              <exceptionHandlers>

The ExceptionPolicy class, like most of the Enterprise Library blocks, uses the Factory pattern to create an implementation class.

C#
private static readonly ExceptionPolicyFactory defaultFactory = 
                        new ExceptionPolicyFactory();

The ExceptionPolicyFactory class in derived from the generic class LocatorNameTypeFactoryBase<T>, which is used to create an instance of the specified type defined by a configuration source. The default implementation of this class uses the ConfigurationSourceFactory, and the ConfigurationSourceSection class, which as the code documentation states: Creates a new configuration source based on the configuration information from the application's default configuration file.

From this, it can be seen why the default implementation of ExceptionPolicy only searches the application configuration file. It is, however, relatively easy to change this.

Using an alternate configuration file

The ExceptionPolicyFactory class has an overloaded constructor that can be used for creating an instance that uses an alternative configuration source, such as an assembly's configuration file, rather than the application's configuration file.

C#
public ExceptionPolicyFactory(IConfigurationSource configurationSource)

The IConfigurationSource interface represents a source for getting configuration information. The Enterprise Library contains five classes that implement this interface:

  • ConfigurationSourceSection
  • NullConfigurationSource
  • ManageableConfigurationSource
  • DictionaryConfigurationSource
  • FileConfigurationSource

The last one, FileConfigurationSource, is what we need for this solution. The constructor for this class takes a single parameter, which is an absolute, or relative, path to the configuration file to be used.

C#
public FileConfigurationSource(string configurationFilepath)

HierExceptionPolicy

This static class implements the HandleException method found in the Enterprise Library's ExceptionPolicy class, but does so in a hierarchical fashion, starting with the configuration file for the assembly it is defined in, and working upward in the application's path. This allows for some policies to be defined at a high level, while also allowing for others to be local to the application.

The class uses a Dictionary, which is keyed to file names to prevent duplication, to store any FileConfigurationSource that has been found. Using lazy initialization, for performance reasons (see warning CA1018), upon first being accessed, the collection is initialized with the configuration file for this assembly. Storing the sources already found eliminates the need to continually search for configuration files on subsequent calls. This still, however, allows for configuration files to be added at runtime, as long as no existing file contains an implementation of the policy trying to be located.

C#
private static Dictionary<string, FileConfigurationSource> ConfigSources
{
  get
  {
    if(m_Sources == null)
    {
      m_Sources = new Dictionary<string, FileConfigurationSource>();
      // Get the path for this assembly
      string filePath = Assembly.GetExecutingAssembly().Location + ".config";
      // Make sure the file exists, and add it to the collection
      if(File.Exists(filePath))
        m_Sources.Add(Path.GetFileName(filePath), new FileConfigurationSource(filePath));
    }
    return m_Sources; 
  }
}

The HandleException method is fairly straightforward; it finds a policy matching the name given, and handles the given Exception. If a policy name is not passed, it will use a default policy name, cleverly called "Default Policy", in this case. A more extensible implementation would store this in the configuration file, but for demo purposes, it is hard coded, and noted in the documentation.

C#
public static bool HandleException(Exception ex, string policyName)
{
  // Check if a valid exception is passed in
  if(ex == null)
    throw new ArgumentNullException("ex");
  else
    ExceptionToHandle = ex;
  
  // If no policy name were passed in, use the
  // default policy
  if(string.IsNullOrEmpty(policyName))
    PolicyName = "Default Policy";
  else
    PolicyName = policyName;
  
  // Get a policy to handle the exception
  ExceptionPolicyImpl policy = GetPolicy();
  if(policy == null)
    throw new NoPolicyException("No policy named \"" + policyName + 
       "\" could be found for the exception " + ex.GetType().Name, ex);
  
  // Handle the exception as per the policy
  return policy.HandleException(ex);
}

The interesting work happens in the GetPolicy method. This is where the code attempts to find a policy, matching the name, in the configuration chain which also handles the exception. If one can't be found, a NoPolicyException is thrown. It's up to the caller to make sure an infinite loop does not occur by trying to handle this exception.

C#
private static ExceptionPolicyImpl GetPolicy()
{
  ExceptionPolicyImpl policy = null;
  
  // Search the existing sources for the policy to handle the exception
  foreach(FileConfigurationSource source in ConfigSources.Values)
  {
    policy = FindPolicy(source);
    if(policy != null)
      return policy;
  }
 
  // Nothing was found so far, so we need to walk the
  // folder chain to find a policy
 
  // In theory there should be one config so far,
  // so start with the siblings at this level.
  string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
  policy = FindConfigFile(path);
  if(policy != null)
    return policy;
 
  return null;
}

As can be seen here, the Dictionary that stores FileConfigurationSources is iterated over first to find a policy in a file that may have already been found. If no policy can be found in the collection so far, which may be the case for a first run, the code then starts at the current folder level and looks for any sibling configuration files.

The FindConfigFile method takes a path and searches for any config files it can find. After checking to make sure the file has not already been added to the collection, the FindPolicy method is called to attempt to find an ExceptionPolicyImpl that can be used to handle the exception. If an ExceptionPolicyImpl can't be found at this level, the code gets the folder at the next level, and, after making sure it has not gone beyond the root of the application, calls FindConfigFile recursively.

C#
private static ExceptionPolicyImpl FindConfigFile(string path)
{
  ExceptionPolicyImpl policy = null;
 
  // Only interested in config files in the directory
  string[] files = Directory.GetFiles(path, "*.config");
  foreach(string file in files)
  {
    // Check if the config file was already added
    if(!ConfigSources.ContainsKey(Path.GetFileName(file)))
    {
      // Create a new source and store it in the collection for later
      FileConfigurationSource source = new FileConfigurationSource(file);
      ConfigSources.Add(Path.GetFileName(file), source);
  
      // Does the source handle the exception?
      policy = FindPolicy(source);
      if(policy != null)
        return policy;
     }
  }
 
  // Get the parent path
  string parentPath = Path.GetDirectoryName(path.Substring(0, 
                           path.LastIndexOf('\\')+1));
 
  // Make sure we have not passed the root folder for the application
  // and call this method recursively if not
  if(!IsPastRootFolder(parentPath))
    return FindConfigFile(parentPath);
 
  return policy;
}

This method will check the FileConfigurationSource to see if the policy exists, and handles the exception. The ConfigurationException is thrown if the policy can't be found in the file, in which case, the code eats it and returns null.

C#
private static ExceptionPolicyImpl FindPolicy(FileConfigurationSource source)
{
  // Create a factory from the source
  ExceptionPolicyFactory factory = new ExceptionPolicyFactory(source);
  ExceptionPolicyImpl policy = null;

  // Try to create a policy implemenatation for the policyname
  // If the policy can't be found in the source, this exception
  // will be thrown
  try
  {
    policy = factory.Create(PolicyName);
  }
  catch(ConfigurationException)
  {
    return null;
  }
 
  // Now that a policy has been found, see if it
  // handles the exception
  if(policy.GetPolicyEntry(ExceptionToHandle.GetType()) != null)
    return policy;
  else
    return null;
}

Conclusion

This project was a simple solution for the requirement to have a global, or enterprise wide, definition for Exception Handling rules, while also allowing for the flexibility to define them at the application level. Possible extensions to this methodology would include the ability to override higher level Exception Polices at the application level.

Of course, this is only one possible solution, and I'm sure there is an easier/better method, but it's the only thing I could think of at the time. Enjoy it, and use it as a learning tool or a stepping stone for other applications.

History

  • Initial post: 10/6/07.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)



Comments and Discussions

 
GeneralVery good tutorial Pin
zdlik26-Mar-09 9:54
zdlik26-Mar-09 9:54 
GeneralRe: Very good tutorial Pin
Not Active26-Mar-09 10:34
mentorNot Active26-Mar-09 10:34 
Generalexample Pin
zdlik25-Mar-09 3:33
zdlik25-Mar-09 3:33 
GeneralRe: example Pin
Not Active25-Mar-09 3:51
mentorNot Active25-Mar-09 3:51 
GeneralRe: example Pin
zdlik25-Mar-09 10:46
zdlik25-Mar-09 10:46 
GeneralRe: example Pin
Not Active25-Mar-09 11:18
mentorNot Active25-Mar-09 11:18 
GeneralRe: example Pin
zdlik26-Mar-09 4:42
zdlik26-Mar-09 4:42 
Yes i debuged. It does not find the web.config.

My i send you my example demo?
thanks

salman

GeneralRe: example Pin
Not Active26-Mar-09 10:35
mentorNot Active26-Mar-09 10:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.