Coding, is? Fun!

Saturday, November 21, 2009

Exception Handling in .NET projects

(Updated below)
Here are a few gudelines for Exception Handling. I learnt some of this from the book ".NET Framework Guidelines".

Here are a few questions: Whenever you write a method in a class, is it a good practice to wrap the code in a Try..Catch..Finally block? For example, let us say you are writing a function, GetProductsByProductId. Should thismethod have a Try...Catch..Finally block?
The answer is No. It should have a Try..Finally block, but the Catch is not necessary for every method.
Do NOT catch an exception unless you have a specific reason for handling it. Put a global exception handler and let such exceptions bubble up and be caught in that.
As a corollary, do NOT ever catch the general exception. That is, do not do this:

try
{
...
...
}
catch(Exception ex)
{
...
}


Exception handling needs some thought to go into it. Have a Catch block only if you have a specific reason for handling a specific exception.
One example is this: let us say you load configuration from a file. If the file is not available, you assume certain defaults. In that case, you can handle the "FileNotFound" exception and take an alternative route.

Throwing exceptions
Generally, do NOT catch an exception, wrap it in a custom exception and rethrow it. Let us say a method is not getting the correct parameters. Then, use the .NET exception "InvalidArgumentException". Do not write a custom exception and throw it.

Custom Exceptions
Custom Exceptions are exceptions in your application code, derived from the base Exception class. They can be created for a few purposes. For example, let us say you are handling concurrency checks in the db layer. In case of a failure, you can throw a custom exception. What is the purpose of a custom exception? Usually an application framework handles unforeseen circumstances through a custom exception so that program flow on higher layers can perform useful actions based on such exceptions. Such exceptions are NOT a substitute for well designed interfaces that return correct values.

Service Layer Exceptions
As a best practice, any service methods should ideally return well designed Error objects instead of relying on Custom Exceptions. Web service methods are system boundaries and should not rely on exception handling for propagating errors.
So, handle general exceptions and log them at the service entry. Then return specific error objects from all methods - such as ones with an error code, message (or message id for localizations) and error type.

TryGet Methods
There are a set of methods such as int.TryParse which simply handle failure through a return boolean value. These kind of methods are conventionally written starting with Try.. such as TryGetConfigValue. These methods need not suppress ALL exceptions. They handle an exception and return an error value only if the primary purpose fails. If you have a TryGetConfigValue method and there is an exception getting the config value, that should be suppressed. But such methods need not suppress all exceptions. Any exception unrelated to their primary purpose CAN propagate.

Update I:
My friend Mr.Adrian Gonzalez suggested a few changes to the above post. I am adding them as edits here:

1. I mentioned above that a try..finally block may be necessary in many methods. The purpose of the "finally" is, of course, so that any cleanup code can reside there. Remember that the finally code is ALWAYS executed.
This used to be the place where developers would close database connections or close filestreams. But, .NET has a much cleaner way of closing costly resources: any object that implements the IDisposable interface can be cleaned up the following way:

using (SqlConnection ObjCon = new SqlConnection(Helpers.GetConnectionString()))
{
//do something
}//connection object is closed and recovered here

An instance of any class that implements IDisposable will support the above syntax. Much cleaner than closing the connection after checking its state in a finally block.
Further, do not throw an explicit exception from a finally block.

2. One of the few places that it is ok to capture a general exception (catch(Exception ex)) is when you want to log an exception at that point. For example, developers do this at a service method's root. That is fine.
When you RETHROW an exception, this is good:

try
{
...
}
catch(Exception ex)
{
throw;//preserves call stack for logging
}

This is bad:

try
{
...
}
catch(Exception ex)
{
throw ex;//does not preserve the call stack for logging
}


3. Always include and copy the inner exception when creating a custom exception. Always log the inner exception, if one is available.

4. I mentioned service methods above. In a WCF based web service, there is a way to include an exception as part of your contract. This is called a FaultContract. This has two benefits - you allow specific exceptions to bubble up to the client, in a strongly typed fashion. The client knows to handle them appropriately, even though they are on the other side of a serialization boundary.
The second benefit is that you can add some specific details about the exception back to the client.
For more details about faultcontracts, refer here: Fault Contract Sample

Labels: ,