The Problem Statement

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:

  1. Lack of a Custom Exception Class
  2. No clearly defined single layer (of the n-Tiers) which was designated to handle all exceptions. All the tiers seemed to be handling one or more exceptions.
  3. Anytime an exception needed to be re-thrown (e.g. SqlExceptions and SOAPExceptions), it was through ‘throw ex’ – instead of just throw.

The Solution

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.

Custom Exception Class

Code Snippet
  1. // This is the main piece of your custom exception class – MyAppException(string message, Exception innerException)
  2. public MyAppException(string message, Exception innerException) : base(message, innerException)
  3. {
  4.     if (innerException != null)
  5.     {
  6.         message += innerException.ToString();
  7.     }
  8. }

n-Tier app – Exception handling

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.

Throw versus ThrowEx

Most exception handling code that you see will look the code below:

Code Snippet
  1. try
  2.    {
  3.         // do some operation that can fail
  4.    }
  5.    catch (Exception ex)
  6.    {
  7.         // do some local cleanup
  8.         throw ex;
  9.    }

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

    1. SqlException – exceptions generated at the Database driver/Data Access Layer .
    2. SoapException – exceptions generated outside of the process boundary altogether – and passed into the CLR as a general SOAP exception

So – what is the solution?

The solution is to use throw instead of throw ex. Throw retains the entire stack trace.

Code Snippet
  1. try
  2.     {
  3.         // do some operation that can fail
  4.     }
  5.     catch (Exception ex)
  6.     {
  7.         // do some local cleanup
  8.         throw;
  9.    }

On catching exceptions in database code (inside a database stored procedure)

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
          dbms_output.put_line('invalid cursor');        
      RAISE;
   WHEN INVALID_NUMBER THEN
          dbms_output.put_line('invalid number');        
      RAISE;
   WHEN ROWTYPE_MISMATCH THEN
         dbms_output.put_line('rowtype mismatch');
      RAISE;
   WHEN TOO_MANY_ROWS THEN
         dbms_output.put_line('too many rows');
      RAISE;
    WHEN CURSOR_ALREADY_OPEN THEN
         dbms_output.put_line('cursor open');
      RAISE;
   WHEN DUP_VAL_ON_INDEX THEN
          dbms_output.put_line('duplicate value');
      RAISE;
   WHEN OTHERS THEN
          dbms_output.put_line('other exception');
      RAISE;
END; -- exception handlers and block end here

Download Sample Solution

Custom Exception Handling download

About the Author

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 – https://www.anujvarma.com/

Anuj holds professional certifications in Google Cloud, AWS as well as certifications in Docker and App Performance Tools such as New Relic. He specializes in Cloud Security, Data Encryption and Container Technologies.

Initial Consultation

Anuj Varma – who has written posts on Anuj Varma, Hands-On Technology Architect, Clean Air Activist.