1

I have a couple of questions around exception management for a website:

  • in the catch block can I have a static class with a handle exception method that handles the exception like so:

       catch (Exception ex)
        {
            throw ExceptionHandler.HandleException(...);
        }
    

    where ExceptionHandler.HandleException is a static method that returns a variable of type System.Exception. Is this a good practice? Any possible problems with this approach? Will it be thread safe?

  • In my application I have a DAL layer that is called by the Business layer and the Business layer is called by the UI. So, is it a good practice to just re-throw all Custom exceptions so they bubble up right up to the UI where they get displayed whereas System.Exception types get logged and I throw a custom exception in the catch block? for eg in DAL and Business Layer like so:

        catch (CustomExceptionBase ex)
        {
            throw;
        }
        catch (Exception sysEx)
        {
            ICustomExceptionBase ex = new SysException(sysEx);
            ex.Handle();
            throw BusinessException("Some problem while serving your request");
        }
    

    In the UI layer like so

        catch (CustomExceptionBase ex)
        {
            //when custom exception bubbles up; code to display text to user on screen
        }
        catch (Exception sysEx)
        {
            ICustomExceptionBase ex = new SysException(sysEx);
            ex.Handle();
            //display error on screen;
        }
    

    Here CustomExceptionBase implements ICustomExceptionBase and inherits Exception. SysException & BusinessException both inherit from CustomExceptionBase.

Thanks for your time...

EDIT The intent of rethrowing in the system.Exceptions block is so that if there is a fatal error like database connection lost or something similar then I log it for the technical helpdesk and return a ticket number and rethrow the same so the user knows that something went wrong and this is your reference number to follow up. For all custom exceptions in the DAL layer or Business layer, I just bubble it up all the way to the UI where the text gets displayed.

user20358
  • 14,182
  • 36
  • 114
  • 186
  • Heh, it'll be thread safe if you make it thread safe. Other than that, I'd say that if you've handled an exception, you shouldn't need to re-throw it. If all you're doing is logging the exception and re-throwing it, the catch block is probably pointless. To my mind, handling an exception means dealing with it in such a way that you won't need to re-throw. – Ade Stringer Feb 16 '12 at 11:49
  • point taken. But here the intent for system exceptions is to 1.) log it which is all what the ex.Handle() would do 2.) alert the user that something went wrong, hence the throw.. – user20358 Feb 16 '12 at 11:56
  • Perhaps Handle() is a misleading name ... – user20358 Feb 16 '12 at 11:57
  • I think it's bad practice to handle all possible `Exception`s (except in some top-level code that warns the user and shuts down the application). Handle only the exceptions that you know how to handle. – svick Feb 16 '12 at 12:21
  • I want to gracefully handle all errors without showing the user the asp.net yellow error screen :) – user20358 Feb 16 '12 at 12:26
  • Sure, but for that you just need to ensure that you've got `` in your web.config (well, a default document would help too, but it's a start). – Ade Stringer Feb 16 '12 at 15:08

4 Answers4

4

I suspect some of the answers at least are entirely down to your architecture. In the first case it all depends on what ExceptionHandler.HandleException does exactly. Does it generate a new exception based on some criteria or is it just going to return the original exception?

Whether its thread-safe or not entirely depends on its implementation. For example in the following trivial case I'd say it was thread safe:

public static Exception ExceptionHandler.HandleException(Exception ex)
{
    return ex;
}

In other cases it could easily be not thread safe. eg:

public static string message;
public static Exception ExceptionHandler.HandleException(Exception ex)
{
    message = ex.ToString;
    sleep(2000);
    return new Exception(message);
}

The latter example clearly has scope for the message variable to be changed by another thread while this one is asleep.

As for the second... Exceptions should be handled where it makes sense to handle them. There is no hard and fast rule. If some part of the code can effect a recovery from an exception (or is willing to skip over it) then catch it at that point and not earlier. If an exception is truly fatal then nothing should be trying to catch it and pretend otherwise so you should just let it bubble right up to the top and do something like alert your user that things have crashed and that you need to restart or whatever.

So really it depends on what your custom exceptions mean. If they just mean "You want to retry this" then that is different from an exception saying "Data integrity has been compromised: 0==1". both of these may be custom so really its for you to decide where to handle things.

Chris
  • 27,210
  • 6
  • 71
  • 92
  • In the first case Handle is calling log4net to log an exception. In the second example if there fatal error like database connection lost or something similar then I log it for the technical helpdesk and return a ticket number and rethrow the same so the user knows that something went wrong and this is your reference number to follow up. – user20358 Feb 16 '12 at 12:07
  • 1
    Your first question then becomes is log4net thread safe. I've never used it myself but I suspect it probably is. You may also want to not throw what is returned from your method but instead just `throw` so that the original exception is passed on with original context. For the second that sounds like a sensible strategy. Its hard to give a definitive answer on something like this though since its all down to individual cases. – Chris Feb 16 '12 at 12:11
1

Yes, you can call a static exception handler inside a catch block and it will likely be threadsafe as long as you don't reference any static variables.

You should look at Microsoft's Enterprise Library. It has nearly this same design but uses exception policies defined in the web.config to control how the exception is bubbled, wrapped or discarded. Couple that with the Application Logging block and you have a complete solution.

Chris Gessler
  • 22,727
  • 7
  • 57
  • 83
1

In itself there aren't any technical problems having a static method to handle exceptions / rethrow exceptions, however from a best practices point of view having a single method that magically "handles" exceptions strikes me as a potential code smell. Exceptions are by their very name exceptional, each individual case requires thought to go into it to make sure that you are doing the correct thing and so I find it unlikely that your HandleException method will always be doing something sensible.

As an extreme example I know of one such application where pretty much every single method was wrapped in a try-catch block with a call to an static exception handler method which threw a generic MyApplicationException type. This is an extremely bad idea:

  • It clutters the code
  • It makes it harder to make sense of stack traces
  • It makes it very difficult for callers to catch and handle specific exceptions types
  • It makes throwing an exception an even bigger performance penalty than before

My favourite was a method which wasn't implemented which looked a bit like this:

void SomeException()
{
    try
    {
        throw new NotImplementedException();
    }
    catch(Exception ex)
    {
        throw ExceptionHandler.HandleException(...);
    }
}

The worst bit of this of course that it is completely mindless. As I said before exceptions are exceptional - each try ... catch block requires careful thought and consideration to be put into how it should behave, the use of a generic HandleException method is an immediate warning flag that this probably isn't the case.

Rethrowing exceptions

Generally speaking you should only rethrow an exception in two cases:

  • When you want to add contextual information to an exception (such as the name of the current file being processed)
  • When you had to catch an exception in order to handle some specific case, e.g. handling an "out of disk space" error

    catch (IOException ex)
    {
        long win32ErrorCode = Marshal.GetHRForException(ex) & 0xFFFF;
        if (win32ErrorCode == ERROR_HANDLE_DISK_FULL || win32ErrorCode == ERROR_DISK_FULL)
        {
            // Specific "out of disk space" error handling code
        }
        else
        {
            throw;
        }
    }
    

"Bubbling" (i.e. catching and rethrowing an exception without doing anything with it) is completely unneccessary - this is what exceptions are already designed to do all by themselves!

Handling exceptions

Other people have said "exceptions should be handled where it makes sense" and I have even given this advice myself, but in hindsight I don't think thats particularly useful advice! :)

What people generally mean by that is that you should handle exceptions for specific reasons, and that you should choose where in your application to handle that exception depending on that reason.

For example if you want to display an error message to inform the user that they don't have permission to modify a file if you get an access denied error then you may have a specific try-catch block in your UI code that does this:

catch (IOException ex)
{
    long win32ErrorCode = Marshal.GetHRForException(ex) & 0xFFFF;
    if (win32ErrorCode == ERROR_ACCESS_DENIED)
    {
        // Display "access denied error"
    }
    else
    {
        throw;
    }
}

Note that this is very specific to this one case that we wish to handle - it catches only the specific exception type were are interested in and performs additional checks to filter down to the specific case we are interested in.

Alternatively if you want to log unhandled errors or gracefully display error messages to the user instead of an IIS 505 error screen then the place to do this is either in Global.asax or through a custom error page - ASP.Net Custom Error Pages

My point is that when handling exceptions we are are thinking carefully about what it is we want to achieve in terms of application functionality (e.g. retry logic, error messages, logging etc...) and implementing our exception handling logic to specifically address those requirements in the most targeted way possible - there is no magic exception handing framework and there is no boilerplate code.

Avoid exceptions entirely whenever possible!

I usually find that the best strategy is simply to avoid exceptions entirely whever possible! For example if your page parses user enter numbers and you want to display validation messages when they enter stupid values then validate your input up-front instead of catching exceptions:

Bad:

void DoSomething()
{
    int age = int.Parse(ageTextBox.Text);
    if (age < 0)
    {
        throw new ArgumentOutOfRangeException("age must be positive");
    }
    if (age >= 1000)
    {
        throw new ArgumentOutOfRangeException("age must be less than 1000");
    }
}

void Button_Click(object sender, EventArgs e)
{
    try
    {
        DoSomething();
    }
    catch (Exception ex)
    {
        DisplayError(ex.Message);
    }
}

Good:

void Button_Click(object sender, EventArgs e)
{
    int age;
    if (!int.TryParse(ageTextBox.Text, out age))
    {
        DisplayError("Invalid age entered");
    }
    if (age < 0)
    {
        DisplayError("age must be positive");
    }
    if (age >= 1000)
    {
        DisplayError("age must be less than 1000");
    }

    DoSomething();
}

Users enter invalid data all of the time - handling this is really application logic and shouldn't fall into the real of exception handling - its certainly not an event that I would call "exceptional".

Of course this isn't always possible, however I find that using this strategy simplifies the code and makes it easier to follow application logic.

Community
  • 1
  • 1
Justin
  • 84,773
  • 49
  • 224
  • 367
  • Thanks Justin for taking the time and providing me with a very detailed answer. While I digest all that you've put in, some questions off the top of my head: I don't intend to throw exceptions for data that can be validated at the UI. This is in the case when in my asp.net MVC application, the Data accesslayer processes some document returns an error code that was interpreted as: "not enough details in document", I then throw a Custom Exception in the DAL, which just gets bubbled all the way to the UI Layer. The IT helpdesk wont be interested in debugging this exception, so I dont log it. – user20358 Feb 16 '12 at 15:45
  • However, if it is an error like Database server down, then that will get caught in the System.Exception catch block. Now here I do two things: log the error with [ex.Handle()] to a text file or event log and then throw a Custom exception with the ticket number or some similar reference appended, so the user gets taken to the website's error page with that information. This exception thrown in the dal layer gets rethrown in the Business layer where it is now available in the UI to be displayed.. – user20358 Feb 16 '12 at 15:46
  • Do you think its bad practice to bubble up an exception by using "throw;" , and the correct way would be, for every method to return an object that contains an ErrorString property? I had read somewhere that throwing exceptions don't cause any measurable performance hit, so I saw no harm in using a throw new CustomExcpetion("") to bubble up the exception instead of creating an extra object to pass the error string up to the UI layer. What other way would you recommend? – user20358 Feb 16 '12 at 15:46
  • @user20358 In your case the parallel would be refactoring your APIs to report the "not enough details in document" case without using exceptions, e.g. by having it return a "task status" object that encapsulates the fact that the processing was not completed and messages that indicate why. It may well be that this is too much hassle for too little gain, however if this exception is never logged then it sounds very much like this its business as usual when this exception is thrown. You then handle other exceptions (e.g. missing database connectivity) globally. – Justin Feb 16 '12 at 15:52
0

First of all we need to consider Exception types, In any business exception can be either BusinessException or Technical/SystemException.

  1. In BusinessException we can send custom exceptions with error details.

  2. In Technical/System Exception we should not handle it, but let it popup to UI layer.UI Can decide what error should be displayed in case of exceptions.

  3. In your approaches, if you handle exception or throw the custom exception the call trace is lost.

Manas
  • 2,534
  • 20
  • 21
  • In custom exceptions the only thing that gets set is an error message property that the coder sends to the class to get set. If the business layer comes across some anomaly in the user input it will flag it as a: throw BusinessExcpetion("Your application was rejected due to your document not having the right information"); – user20358 Feb 16 '12 at 12:20
  • this is when the business layer needs to send some document for processing to another service and the service returns an error that was interpreted as above. here it just gets bubbled all the way to the top. The IT helpdesk wont be interested in debuggin this exception – user20358 Feb 16 '12 at 12:23
  • when the service is down I want to tell the user... hey someting went wrong somewhere. We have logged it and here is your ticket number if you want to check back with us later.. so in this case ex.Handle() will log the error to a text file or event log and then throw a business exception with the ticket number appended so the user gets taken to the website's error page with that information. – user20358 Feb 16 '12 at 12:25
  • In this scenario, you can log the error details (you can use Microsoft.ENterpriseLibrary or log4net). You can throw a business exception to UI Layer and show appropriate message.\ – Manas Feb 16 '12 at 12:43