You have just started working on an existing codebase – an n-Tier project – and noticed that the Exception Handling framework wasn’t as well defined as you would expect. Some specific things that bother you include:
Each of the issues listed above are discussed in this post. A sample solution that defines a custom exception class – along with a simple client showing its usage – is included. This can be used as a starting point for any Custom Exception handling module inside a .NET project.
Q) Which tier (layer) should you catch the exception in?
A) Always the business tier. Let the Data Access Layer and any other layers (e.g. the persistence/entity layer) rethrow the exception upwards. Then – in the business layer, wrap the caught exception in your custom exception (e.g. MyAppException) using the method shown above (which appends all the inner exceptions to the MyAppException).
Q) How and where should one display the exception message?
A) Certain exceptions have no business being displayed to the end user. Simply log these in an exception log – and do not rethrow these exceptions. However, a good set of exceptions do need to be displayed to the end user. The exception that we previously caught in our business layer – simply needs to be rethrown with a user-friendly message. This is then is caught in the presentation layer (webforms or winforms) – and the friendly exception message is displayed.
Most exception handling code that you see will look the code below:
With the code above (throw ex), the stack trace is truncated – and only offers information starting from the method that failed. The origin of the exception will always appear to be in application code. However, as we all know, this isn’t always true. Exceptions can originate in various external systems – and eventually get thrown as CLR exceptions. Some common examples include
The solution is to use throw instead of throw ex. Throw retains the entire stack trace.
If you have part of your business logic in a stored procedure (there are good reasons why this provides the best data access layer performance), then chances are your stored procedure is doing its own exception handling. It may be throwing and catching its own database layer exceptions. This is a terrible idea – considering the number of possible different exceptions that can occur in a database. Everything from integrity constraints being violated to incorrect SQL syntaxes to what not. To even attempt to identify the root cause of such errors without the database specific debug information – is nightmarish. I was part of a project where developers routinely went through this nightmarish exercise.
The exercise is meaningless since it is trivial to simply rethrow the stored proc exception to the database driver (and hence up to the data access layer). This provides meaningful information to a developer who needs to identify the source of the error. This changes the entire troubleshooting sequence from a ‘trial and error’ investigation – to a ‘simply look at the detailed exception – e.g. integrity constraint violated’.
Once I made these changes to all the stored procedures (simply re-threw all exceptions in the catch blocks on the stored proc exception handling), life became a lot simpler for the development team on the project. There is no reason to let the database stay secretive about its exceptions. Just throw them up to the data access layer – to provide meaningful troubleshooting info.
An example of how to rethrow exceptions in PLSQL code is shown below (use the RAISE keyword)
DECLARE pe_ratio NUMBER(3,1); BEGIN SELECT price / earnings INTO pe_ratio FROM stocks WHERE symbol = 'XYZ'; -- might cause division-by-zero error INSERT INTO stats (symbol, ratio) VALUES ('XYZ', pe_ratio); COMMIT; EXCEPTION -- exception handlers begin WHEN ZERO_DIVIDE THEN -- handles 'division by zero' error INSERT INTO stats (symbol, ratio) VALUES ('XYZ', NULL); COMMIT
WHEN INVALID_CURSOR THEN
WHEN INVALID_NUMBER THEN
WHEN ROWTYPE_MISMATCH THEN
WHEN TOO_MANY_ROWS THEN
dbms_output.put_line('too many rows');
WHEN CURSOR_ALREADY_OPEN THEN
WHEN DUP_VAL_ON_INDEX THEN
WHEN OTHERS THEN
END; -- exception handlers and block end here
Custom Exception Handling download
Anuj Varma is a technical architect specializing in Microsoft Platforms. Along with high level design and overall architecture, he provides hands-on development, unit testing and other best practices on all the projects he works on. His troubleshooting extends to n-Tier applications, WCF and Azure based applications to advanced SQL Server performance issues.
His customer base includes DELL, Schlumberger, British Petroleum as well as several small to midsize companies across Texas. Anuj can be contacted via the contact form on his blog – http://www.anujvarma.com/