May 21, 2019
Hot Topics:

Perform Exception Handling in .NET Exceptionally

  • November 10, 2004
  • By Mark Strawmyer
  • Send Email »
  • More Articles »

Creating Custom Exceptions

As a rule of thumb, you should always try to use the pre-defined exception types. However, sometimes creating your own custom exceptions has its advantages. Microsoft recommends deriving custom exceptions from the ApplicationException class rather than the System.Exception class. This allows you to differentiate between the exceptions your application throws and those that originate from within the runtime system.

Custom Exception Sample Code

A useful example of a custom exception (right or wrong) is one you'd use in a data access layer. When an exception occurs, including the offending SQL statement within the exception is typically useful to ensure that the exact SQL statement can be debugged. As an example, the following code creates a custom exception that allows you to include the SQL statement being executed. All you have to do is extend the ApplicationException base class to include your additional property:

using System;using System.Runtime.Serialization;namespace CodeGuru.ExceptionHandling{/// <summary>/// The exception that is thrown when the database layer returns a/// or error./// </summary>[Serializable]public class DatabaseException : ApplicationException{   private string _CommandText = "";   /// <summary>Get the SQL command text that caused the   /// exception</summary>   public string CommandText   {      get { return this._CommandText; }   }   /// <summary>   /// Constructor   /// </summary>   public DatabaseException()   {   }   /// <summary>   /// Constructor   /// </summary>   /// <param name="Message">Message that describes the current   /// exception.</param>   public DatabaseException(string Message) : base(Message)   {   }   /// <summary>   /// Constructor   /// </summary>   /// <param name="Message">Message that describes the current   /// exception.</param>   /// <param name="InnerException">System.Exception event that   /// caused the current exception.</param>   public DatabaseException(string Message,                            Exception InnerException) :                            base(Message, InnerException)   {   }   /// <summary>   /// Constructor   /// </summary>   /// <param name="Message">Message that describes the current   /// exception.</param>   /// <param name="SqlCommandText">SQL command text that caused   /// the exception.</param>   public DatabaseException(string Message, string SqlCommandText) :                           base(Message)   {      this._CommandText = SqlCommandText;   }   /// <summary>   /// Constructor   /// </summary>   /// <param name="Message">Message that describes the current   /// exception.</param>   /// <param name="SqlCommandText">SQL command text that caused   /// the exception.</param>   /// <param name="InnerException">System.Exception event that   /// caused the current exception.</param>   public DatabaseException(string Message, string SqlCommandText,                            Exception InnerException) :                           base(Message, InnerException)   {      this._CommandText = SqlCommandText;   }   /// <summary>   /// Constructor   /// </summary>   /// <param name="SerializationInfo">Holds the serialized object   /// data about the exception being thrown.</param>   /// <param name="StreamingContext">Contains contextual   /// information about the source or destination.</param>   protected DatabaseException(SerializationInfo SerializationInfo,                               StreamingContext StreamingContext) :                              base(SerializationInfo,                                   StreamingContext)   {   }}}

Now that you've defined your custom exception class, you can use it. The following sample code tries to connect to a database (SQL Server with the Northwind database) and execute a SQL statement:

SqlConnection connection = new SqlConnection(   "Server=localhost;Database=Northwind;Integrated Security=false;    User Id=sa;Password=;");System.Text.StringBuilder sqlStatement =    new System.Text.StringBuilder("SELECT * FROM FakeTable");try{   SqlCommand dbCommand = new SqlCommand(sqlStatement.ToString(),                                         connection);   dbCommand.CommandType = System.Data.CommandType.Text;   SqlDataAdapter adapter = new SqlDataAdapter(dbCommand);   System.Data.DataSet data = new System.Data.DataSet();   adapter.Fill(data);}catch( SqlException exception ){   throw new DatabaseException(      exception.Message, sqlStatement.ToString(), exception);}finally{   if( connection != null ) connection.Dispose();}

The SQL, or lack of a database, will cause an exception and provide an opportunity to catch it and wrap the exception in your custom DatabaseConnection. This demonstrates how you can use exceptions in your data access layer and wrap them in a custom exception.

Following Best Practices and Establishing Standards

As with many aspects of programming, you should establish and follow a set of coding standards around exceptions. Establishing standards helps maximize performance and provides the most value from exception usage. Two of the more important items to focus on are when to use exceptions and the method for trapping them.

When using exceptions, you should establish a policy for when to throw an exception, what to put into the exception, and when to create a custom exception. Exceptions have performance implications because you incur overhead when creating and handling them. Thus, if it you can check whether a particular exception state would occur prior to executing the code, you should check for the condition first rather than allowing an exception to occur—especially if you're executing in a loop.

For example, when you want to open a file, you can avoid the FileNotFoundException by using the File.Exists method to determine whether or not the file exists prior to opening the file. Along the same lines, you should avoid using exceptions for common or expected error conditions. Rather than throwing an exception, you should consider returning null or some other sort of error condition. As an example, rather than throwing an exception, String.IndexOf returns -1 if the desired match string does not exist because it is fairly common for the match string not to exist in the source string. An example of where this isn't followed and probably should be is when it comes to checking whether a particular object is a number, which is a common step in many applications that accept input. The only way to check is to try and convert the object to a number with one of the methods in System.Convert, which throws an exception if the object is not a number. Hopefully, Microsoft will address this in future versions of the .NET Framework.

Another important item you should be consistent about is the type of information you put into an exception. All too often, programmers put cryptic or unhelpful information into exceptions. Messages such as "An error has occurred." are not all that helpful. A common tendency is to catch an exception when it occurs and then throw a new exception in its place. If you don't include enough relevant information in your exception, it will be hard for other programmers to determine what caused it or what to do about it. Always test out the relevance of the contained information by logging it to wherever you trap your application logs. The litmus test is whether or not the log provides enough information to diagnose and debug the exception. Additionally, if you don't have anything relevant to add to the exception thrown, nor are you planning to recover from it in the area where it is being trapped, then why trap and re-throw it in the first place? You're better off just allowing the exception to pass back up the calling stack unchanged.

Future Columns

The topic of the next column is yet to be determined. If you have something in particular that you would like to see explained here, you could reach me at mstrawmyer@crowechizek.com.

About the Author

Mark Strawmyer (MCSD, MCSE, MCDBA) is a senior architect of .NET applications for large and mid-size organizations. He is a technology leader with Crowe Chizek in Indianapolis, Indiana. He specializes in architecture, design, and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C#. You can reach Mark at mstrawmyer@crowechizek.com.

Page 2 of 2

Comment and Contribute


(Maximum characters: 1200). You have characters left.



Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date