Reduce Exception Handling Bloat

Most developers are probably familiar with exception handling guidelines that advise you to “avoid catching System.Exception or System.SystemException, except in top-level exception handlers”, and “if you can’t handle the exception in your part of the chain, throw it upwards rather than swallow it”. These spirit of these guidelines basically encourage you to:

  • Create your own typed exceptions rather than throwing “new Exception” (but this heuristic is often followed loosely).
  • Not swallow exceptions too early (propagate to appropriate layer, don’t hide them).
  • Properly handle exceptions by type in an intelligent manner (different exceptions may require different behavior or reporting).
But what you won’t find as often is guidance on how to avoid duplication of code in your exception handlers.

 

Anti-pattern: Exception Handling Boilerplate Code

I’ve often seen code bases littered with repetitive try-catch blocks like this:

try
{
 . . .
}
catch (ArgumentException argEx)
{
    TraceEx.LogTrace(argEx, true, String.Empty, Page.User.Identity.Name);
    HttpContext.Current.ApplicationInstance.CompleteRequest();
    _terminate = true;
}
catch (SystemException sysEx)
{
    TraceEx.LogTrace(sysEx, true, String.Empty, Page.User.Identity.Name);
    HttpContext.Current.ApplicationInstance.CompleteRequest();
    _terminate = true;
}
catch (Exception ex)
{
    TraceEx.LogTrace(ex, true, String.Empty, Page.User.Identity.Name);
    HttpContext.Current.ApplicationInstance.CompleteRequest();
    _terminate = true;
}

In the above example, note that all the code blocks are identical. The only variation points are the exception type and the superficial local name of the exception reference. It bugs the heck out of me when this pattern is repeatedly copied and pasted throughout an application. Part of the problem is that most developers are aware that you should “catch specific exceptions that you anticipate first (rather than always default to System.Exception)“, and of course, order your catch blocks in descending order of the inheritance chain. So in that regard, yes, the above code meets that guideline, but only superficially.What is lost in translation is that the above pattern is meaningful if the catch blocks actually does something different for each exception type; otherwise, those extra catch blocks are redundant and adds little value.

Because all three catch blocks do the same thing, the above code can be simplified to this:

try
{
. . .
}
catch (Exception ex)
{
   TraceEx.LogTrace(es, true, String.Empty, Page.User.Identity.Name);
   HttpContext.Current.ApplicationInstance.CompleteRequest();
   _terminate = true;
}

NOTE: Just in case there is any confusion over “exception type” I would like to point out the following:if the exception thrown is ArgumentException, SystemException, or whatever, “Exception ex” will still be the original type; there is no downcasting to the base System.Exception going on. Recall that unlike C++, with C# there is no “object slicing” scenario going on: you can catch, query, filter, or reflect objects by a base type or interface, but the underlying type information (and the object itself) is there and complete, not in sliced form.

OR you could partially compromise on your DRY principles in order to explicitly declare the different exceptions you will handle. Factoring out duplicate code into a method is a tried-and-true standby everyone understands:

try
{
. . .
}
catch (ArgumentException ex)
{
   HandlePageError(ex);
}
catch (SystemException ex)
{
   HandlePageError(ex);
}
catch (Exception ex)
{
   HandlePageError(ex);
}

void HandlePageError(Exception ex)
{
   TraceEx.LogTrace(es, true, String.Empty, Page.User.Identity.Name);
   HttpContext.Current.ApplicationInstance.CompleteRequest();
   _terminate = true;
}

This may sound remedial or obvious, but far too often I see exception handling code copied-and-pasted. If redundancy isn’t ok anywhere else in your app, why let it go in exception handling blocks?

Unnecessary duplicate code like this is easy to spot and remove. At the very minimum, avoid writing new code that propagates this redundant boilerplate;  adhere to basic DRY and YAGNI principles. The “it’s just a template in case we need to change the behavior” argument just doesn’t cut it: It’s still bloat that clutters and inflates your code making it harder to maintain and read, while adding no real value.

Consider removing such bloat as part of your refactoring strategy.

 

A Better Way: Aspect-Oriented Programming (AOP)

Since exceptions are a cross-cutting concern, you should have a coherent strategy on managing them. A natural consequence of doing so will be smaller, easier-to-read code. One approach is to use Microsoft Enterprise Library’s Exception Handling Application Block (call ExceptionPolicy.HandleException) which utilizes a declarative approach. Details on logging, wrapping, and propagating the exception and be declared based on the exception type and context, effectively simplifying your exception handlers and reducing the size of boilerplate exception handling blocks. Conceptual example:

try
{
}
catch (Exception ex)
{
    if (ExceptionPolicy.HandleException(ex, _accessPolicy))
    {
        throw;
    }
}

Details such as logging and when to handle (throw up the chain or stop) is managed per _accessPolicy and exception type. You would still need to do local cleanup, but the boilerplate stuff is declared in your exception handling policy.

A more general alternative to using the Microsoft Enterprise Library’s Exception Handling Application Block is to leverage the “dynamic interception” feature of a Dependency Injection Container(not all DI containers support this, but most do). Chapter 9, Interception in Mark Seeman’s book Dependency Injection in .Net does an excellent job describing the concept. To get a quick feel of what I’m talking about, you can also take a look at these pages:

Yet another approach is to use an AOP framework. PostSharp is one of the most popular. (See Aspect Oriented Programming Using C# and PostSharp and Aspect-Oriented Programming vs Dependency Injection). Although PostSharp is an elegant solution, here are the the downsides: it isn’t free, your code will be tightly coupled to vendor, and the implementation relies on post-compilation to turn passive attributes into active code.