Exception handling in n-tier applications?

flesh picture flesh · Nov 6, 2010 · Viewed 13.7k times · Source

What is the suggested approach or best practice for handling exceptions in tiered applications?

  • Where should you place try/catch blocks?
  • Where should you implement logging?
  • Is there a suggested pattern for managing exceptions in n-tiered applications?

Consider a simple example. Suppose you have a UI, that calls a business layer, that calls a data layer:

//UI
protected void ButtonClick_GetObject(object sender, EventArgs e) 
{
    try {
        MyObj obj = Business.GetObj();
    }
    catch (Exception ex) {
        Logger.Log(ex); //should the logging happen here, or at source?
        MessageBox.Show("An error occurred");
    }
}

//Business
public MyObj GetObj()
{
    //is this try/catch block redundant?  
    try {
        MyObj obj = DAL.GetObj();
    }
    catch (Exception ex) {
        throw new Exception("A DAL Exception occurred", ex);
    }
}

//DAL
public MyObj GetObj()
{
    //Or is this try/catch block redundant? 
    try {
        //connect to database, get object
    }
    catch (SqlException ex) {
        throw new Exception("A SQLException occurred", ex);
    }
}

What criticisms would you make of the above exception handling?

thanks

Answer

EMP picture EMP · Nov 6, 2010

My rule of thumb is generally to catch exceptions at the top level and log (or otherwise report) them there, because this is where you have the most information about the the error - most importantly the full stack trace.

There may be some reasons to catch exceptions in other tiers, however:

  1. The exception is actually handled. Eg. a connection failed, but the tier re-tries it.
  2. The exception is re-thrown with more information (that isn't already available from looking at the stack). Eg. the DAL might report which DB it was trying to connect to, which SqlException wouldn't tell you.
  3. The exception is translated into a more general exception that is part of the interface for that tier and may (or may not) be handled higher up. Eg. the DAL may catch connection errors and throw a DatabaseUnavailableException. The BL may ignore this for operations that are not critical or it may let it propogate for those that are. If the BL caught SqlException instead it would be exposed to the implementation details of the DAL. Instead the possibility of throwing DatabaseUnavailableException is part of the interface of the DAL.

Logging the same error at multiple tiers is generally not useful, but I can think of one exception: when a lower tier doesn't know whether a problem is critical or not it can log it as a warning. If a higher tier decides that it is critical it can then log the same problem as an error.