tags:

views:

433

answers:

3

In the application architecture guide v2.0a on page 196 (or follow this link), the authors mention this guideline:

"Design an appropriate exception propagation strategy. For example, allow exceptions to bubble up to boundary layers where they can be logged and transformed as necessary before passing them to the next layer."

What is the best way to accomplish this in .NET (C#) ?

A: 

You mean, besides try/catch?

The concept of a layer boundary has no representation in C#. You'll have to design your code so that you can see the layer boundaries, then put appropriate try/catch blocks at those boundaries.

The Exception Handling Application Block in the Enterprise Library is good for this, as it allows you to configure how exceptions shall be wrapped.

John Saunders
All languages have the concept of boundaries, as expressed in some form of external contract. Admitedly, C# is not a "boundary language" whatever that would mean, but the architecture guide is not a guide in C# anyway.
Gregory A Beamer
Gregory, I disagreee. There are no language constructs in C# that represent a boundary or a contract. There are things that can be used to construct boundaries or contracts, but they are not inherently boundaries. So a developer must find the boundaries to put exception handling there.
John Saunders
+1  A: 

What he is saying is you do not capture the errors in every class. Instead, you get to a boundary condition. In general, if you think of this as UI, and include web services (ASMX or WCF) as UI, then you capture at those layers.

In a web application, the only boundary might be the UI, so you will only capture exceptions on the web site itself.

In a distributed application, you will likely have service boundaries (web services, as mentioned) and you capture there.

What you do not want to do is have every class up the stack capturing, logging and throwing the same exception, as you just end up with far too much noise and not enough signal.

Gregory A Beamer
The authors are saying "bubble up to the boundary layer". A layer can be a logical layer (in terms of namespaces) or physiscal layer (assembly level). I found the guideline too abstract to get a better understanding of it.
Patrick Peters
+1  A: 

I think when they talk about a layer boundary they are talking about places where the security sensitivity changes or the call is coming from outside your scope of influence. The main concern is that security sensitive information does not leak out of your application in exceptions. So for example if an error is for a database connection the exception message might be "Unable to access database on serve ServerName". If this kind of exception gets out of your application it gives a hacker the name of you database server which is a bad thing.

These security boundaries are normally at the process boundary. On server applications that would mean any public web services, remote procedure calls or that kind of thing. Also in Web and Win-Forms applications there are global error handlers that handle any un-handled exceptions. I would not expect an in-process dll to include such a boundary.

Once you have identified each of boundary entry points you should use a try catch and then pass the exception to the Microsoft Exception Handling Block. The Exception Handling block allows logging of exceptions and allows you to decide which exception details get thrown to the user. This helps to ensure that security sensitive information does not leak out of your application in exception stack traces.

Global Exception Handler in WinForms:

In WinForms there are two global exception handlers these are defined as events that fire whenever there is an unhandled exception. The best thing to do is to bind to these in your main() function before the Application.Run statement like this:

    static void Main()
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);

        Application.Run(new Form1());
    }

Using the exception application block you would then handle these events like this:

    using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;

    // ...

    static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
    {
        bool rethrow = ExceptionPolicy.HandleException(e.Exception, "Thread Policy");
        if (rethrow)
        {
            Application.Exit();
        }
    }

    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        ExceptionPolicy.HandleException((Exception)e.ExceptionObject, "App Policy");
        // This event always exits the 
        // application so there is no point in looking at 
        // HanleException result.
    }

The difference between these two events is that the ThreadException event only handles events that are created by WinForm's threads. It will also allow the application to continue if you do not rethrow the exception in the event. The UnhandledException event will catch all exceptions in the process but does not allow you to continue so it is only good for logging.

Finally to get the MS Exception block to work you need to add some configuration to the App.Config file like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=2.0.0.0, Culture=neutral, PublicKeyToken=e99c4c62013e92da"/>    
  </configSections>

  <exceptionHandling>
    <exceptionPolicies>
      <add name="App Policy">
        <exceptionTypes>
          <add name="App Exception" type="System.Exception, mscorlib" postHandlingAction="None" />
        </exceptionTypes>
      </add>
      <add name="Thread Policy">
        <exceptionTypes>
          <add name="App Exception" type="System.Exception, mscorlib" postHandlingAction="None" />
        </exceptionTypes>
      </add>
    </exceptionPolicies>
  </exceptionHandling>
</configuration>

You would normally do a far more sophisticated exception policy than this, but I won't go into that here.

Global Exception Handler in ASP.Net

In ASP.Net things are a little easier. All you need to do is implement the Application_Error event in the Global.asax file. To get the last error call Server.GetLastError() and to continue without rethrowing the exception call Server.ClearError(). So your code would look something like this:

<%@ Application Language="C#" %>
<script runat="server">        
    void Application_Error(object sender, EventArgs e) 
    {
        Exception ex = Server.GetLastError();
        bool rethrow = ExceptionPolicy.HandleException(ex, "App Exception");
        if (!rethrow)
        {
            Server.ClearError();
        }
        Response.StatusCode = 500;
    }       
</script>
Martin Brown