Tips and recommended practices for handling exceptions and validations in .NET C#No Comments
This time I wanted to show the research we made with my colleague David Frassoni about recommendations and tips when handling exceptions and validations in C#.
When to Throw an Exception
We should not throw exceptions for every erroneous event. For example in an application in which we want to get a list of students, and there are no students matching with the user’s search, we should not throw a custom exception (e.g.: NoStudentsFoundException), because it’s not actually wrong. In this case it’s recommended to return a state value (e.g.: an empty students list or “null”).
When throwing an exception, don’t use “throw new Exception()”, it’s too generic. We should use the most accurate exception possible according to the operation we are handling.
When throwing an exception in a catch statement, we should append the Inner Exception to keep track of the initial cause.
There are situations where is not recommended to append the Inner Exceptions. For example, in a multi-layer application, if an exception occurs while performing a specific query to the database, if we re-throw the exception through the entire application until we reach the UI layer, we can expose sensitive information to the user. It’s important to be aware of this and know when to stop handling the Inner Exception through layers, and expose to the user a clear message of the exception, without sensitive information. We will talk about this later in this post.
Handling Multiple Exceptions Type
In case we need to handle each different exception in a different way, we should use one single try with several catch statements below it, one for each different type of exception that may occur in the code within the try. Also the catch statements should be placed in order, from the most specific to the most generic one, to handle the specific exception before it is passed to a more general catch block. The finally statement should be used to de-allocate resources, if applies.
To avoid unnecessary exception handlings, external data should be always checked before operating with it, even if it was a file that we’ve just written, or properties we’ve just received from a form. We should never trust anything coming from outside our application. Sometime, as we may use that information to hit a data source, they can contain unexpected code that can harm it, or even delete it.
Disposing Objects Properly
We should use the using statement in as many places as we can in our code, when we are dealing with Disposable objects. This way we prevent resource leaks even in the presence of an exception.
Validating vs Throwing Exceptions
There are some situations where it’s better to programmatically check for a condition in a block instead of catching an exception that eventually would be thrown. We can simply use an if statement to check if a condition matches a proper state, and then do something, instead of catching the exception that will be caused if not using the if statement. The fact that allows us to choose one or the other method is how often we expect this event to occur:
- If the event occurs in exceptional cases, use exception handling with try/catch/finally statements. This way is better than programmatically check the condition because less code is needed in the success case.
- If the event occurs often, use programmatic checks. This way is better than handling the exception because that will require more processing effort.
After an exception handling, we should check that we came back into a valid state, and no undesired effects occurred as a consequence of the exception. For example, if working with a transaction to a database, ensure that the atomicity principle is applied.
We can create a custom exception when the exception types given by .NET Framework do not fit our needs. When creating custom Exceptions:
- Define new exception types only for programmatic scenarios.
- Inherit from the Exception class. It was originally thought that custom exceptions should derive from the ApplicationException class; however in practice this has not been found to add significant value.
Exception Messages Format & Logging
Messages thrown in exceptions should be grammatically correct, concise about the problem and end with a period.
We should always log the exceptions the application throws. When doing this, we should log Exception.ToString(), not only Exception.Message, because this way we can obtain the stack trace, the inner exception and the message.
Handling exceptions within different layers
In most common applications with an infrastructure layer, a business layer and a high-level layer, there are some recommendations to perform a good exceptions handling:
In the Infrastructure layer, lots of exceptions throws are performed, because it deals with the data sources, and it’s more prone to exceptions. At this level, there are practically no catching operations.
As regards the business layer, it serves more for validations and filtering of exceptions messages. Here is where we should resolve all the business requirements, validating programmatically our code blocks. We should catch exceptions coming from the infrastructure layer here for not exposing sensitive information in exceptions messages to the high-level layer.
Finally, in the high-level one, we will perform mainly catches to handle exceptions coming from the infrastructure or business layer. At this point, no exceptions should be thrown, because we only have to show the information to the user. If an error occurs, we should warn the user about it in a friendly message.
There are some frameworks and libraries you can use to deal with exceptions. Some of them are:
- Microsoft Enterprise Instrumentation Framework
- Apache log4net
- Microsoft Enterprise Library
- ELMAH (Error Logging Modules and Handlers)
- System Monitoring Components
Leave a comment
Your email address will not be published.