The developer who has never had to troubleshoot or debug an application is a rare person indeed. Even the best-planned projects can experience situations in which something does not work as expected.
Troubleshooting and debugging an application is a normal process when creating or modifying an application. Fortunately, Microsoft Visual Studio presents many ways to trace through your code while the application is running. Traces can help you understand what is occurring inside your application. The ability to capture and identify errors in both development and production environments is a powerful tool that will help ensure that ASP.NET MVC correctly manages application.
Design an exception handling strategy
Exceptions are a standard part of applications. Even if your software is error free, your application can throw exceptions or experience other problems due to external factors, such as improper data input or network problems.
As you design your ASP.NET MVC application, you should consider potential problem points, such as calling a web service for data or accessing files on a file share, and determine what you will do when an error occurs. Also, consider whether you will notify users and, if so, what you will display in a message. You also need to choose recovery options to keep your application in a normal running state and the kind of diagnostic information you should capture.
Handling exceptions across multiple layers
There are different places in an application in which you can throw an exception, and what you do with those exceptions will be affected by where the exceptions were thrown. When you are writing an application that enforces separation of concern, determining how best to manage errors is complicated by this separation. If all work is done in the same layer, managing errors is easy because you do not have to negotiate boundaries.
However, multiple layers’ complicate things and require you to understand layer rules. A layer should know only about the layer it communicates with, and it should have no knowledge about layers that might be calling it. A traditional three-tier application, shown in Figure, has a data layer, a business layer, and a user interface (UI) layer. The data layer doesn’t know anything about the other layers, the business layer knows about the data layer but nothing about the UI, and the UI layer knows only about the business layer but not the data layer.
Because of the relationships between the layers, errors in the data layer will make no contextual sense in the UI. They do, however, make sense to the business layer. This shows one of the primary architectural considerations when needing to handle exceptions across multiple layers. A layer should ensure that no errors pass through. The business layer, for example, should capture all data layer errors. It should do whatever work needs to be done as a result of those exceptions and determine whether an exception should be sent to the UI for rendering to the user.
Consider this example: A user comes to your website and attempts to log in. Your website, or your complete ASP.NET MVC application, is acting as the UI layer in an n-tiered application and calls the business layer with the login information. The business layer reaches out to the data layer to determine whether there is a match. However, the database server is down, so when the business layer makes its call, it gets an error. There are several choices at this point. You can pass the error up to the UI layer, or analyze it in the business layer and decide what to tell the UI layer. Perhaps your application throws an EntitySqlException or SqlException.
Does your user need to know that information? Probably not, so it would make sense to capture the error when you get it and then log it. However, because it is a fatal exception, you need to tell the user something. It would be reasonable to throw a new custom exception such as a DatabaseException. Because this would no longer be a data layer exception, it would be sensible for the user interface to manage it.
The same approach is appropriate when working within your ASP.NET MVC application. Model errors, for example, should be managed by the controller. If work needs to be done to manage the error, logically it should be managed in the controller. Controller errors are usually propagated out of the controller and into the processing layer. You will typically be catching and managing these errors through the use of MVC-specific error handling protocols.
Displaying custom error pages, creating your own HTTPHandler, and setting Web.config attributes
Although IIS comes with default error pages, it is rare for those pages to look like they belong to an application and to provide the appropriate level of information to the users. Developers customize the error pages as part of their error management and handling process.
You need to determine which errors will have custom pages and what kind of information should be displayed on the pages. When implementing custom error pages, there are at least two primary error pages: one to handle 404 Page Not Found errors and a more generic error display page. However, your application might need to display different information based on the error condition or the portion of the site the user is visiting. You can create these pages as you would any other ASP.NET MVC page: with a view and a controller. You can also pass in a model from your error handler that contains the error information to display information that might be useful to display to the user.
Let’s look at an MVC application with a standard routing construct. In this series of examples, we use a controller called ErrorManagerController that has various action methods for each of the HTTP statuses, such as Status400, as well as a default general action method of ServerError to manage the display of custom error pages.
The Global.asax page is one of the ways you can support custom error pages. Because the ASP.NET MVC framework is based on ASP.NET, there are some shared features, especially in the Global.asax file. The Application_Start method is the most common one used in both ASP.NET and ASP.NET MVC. You can also use the Application_Error method, a global error handler that is called when an unhandled error makes it through the application stack. You can manage an error using the Application_Error method in the Global.asax file.
The method gets the last exception on the server, logs the information, clears the error, and then forwards the user back to the custom error page. In this case, the user is just redirected to the ErrorManager controller’s ServerError method. However, more logic could be put into the method to redirect the user to more applicable error pages. This decision could be based on the type of error as well as whether you need to pass the error to the controller, in which it can determine what, if anything, should be displayed to the user.
You can also set error information in the Web.config file by adding error nodes to the <customErrors> section of the <system.web> area. The following example redirects the appropriate status code to the indicated URL:
The customErrors element has two attributes that are of interest: mode and defaultRedirection. There are three values for mode: On, Off, and RemoteOnly. On and Off specify whether custom errors should be used. RemoteOnly specifies that custom errors are displayed only to remote users while standard error pages are shown to local users. RemoteOnly is the default setting. The defaultRedirection attribute gives an overall handler. If an error occurs that is not handled with a more specific error element, this is the URL that will be presented to the user. HTTP 500 errors are generally handled through other means than configuration, such as filters or OnException handlers. You must set <httpErrors errorMode=”Detailed” /> in the <system.webServer> section of Web.config as well.
Handling first chance exceptions
First chance exceptions are exceptions before they have been handled by an error handler. Every error that occurs in an application begins the error-handling process as a first chance exception. You should try to detect first chance exceptions during the development process to determine how and why they are occurring. You can also capture exceptions during the application runtime and evaluate them at that point.
To configure Visual Studio to detect first chance exceptions, ensure that the Thrown box is checked for the Common Language Runtime Exception row in the DEBUG Exceptions dialog box, as shown in Figure.
FIGURE Enabling detection of first chance exceptions in Visual Studio
When you make this selection, every exception thrown while running in debug mode will be captured by the debugger. You can examine the exception as soon as it is thrown to find and manage other exceptions that might be handled but should not be occurring, or to trace the error through the application flow to ensure that it is properly handled. Figure shows the outcome of an exception. As soon as it is thrown, the debugger catches the exception and displays it.
The ability to catch first chance exceptions in Visual Studio is a significant advantage. It helps you find problems that might be mistakenly hidden, such as those handled by an empty catch block or those that are entirely mishandled. Because exceptions affect performance, you can find errors that are being handled but should not be occurring, such as when trying to parse an object. You can identify those items and correct them, by changing the Parse method to a TryParse method, for example.
Capturing first chance exceptions in the debugger gives you the opportunity to manage and control exceptions before they make it to the production environment. However, that does not mean you can find all errors. An unexpected condition in production can cause a special error condition that did not occur during development. Fortunately, you can catch first chance exceptions in your ASP.NET MVC application by inserting code in the Global.asax file. Demonstrates how to handle FirstChanceExceptions in your application by setting the event handler.
Using first chance exception functionality enables you to add logging or some other error management technique into your application that will be called whenever an exception in your application is thrown. This is just a notification, however, and it can cause its own set of issues. You also have to be careful when managing code within the FirstChanceException method because an error in that method causes a FirstChanceException to be called. This results in a StackOverflow exception because of the recursive calling of FirstChanceException.
The notification of the error does not do or allow anything to handle the error; it is simply a notification that the error has occurred. After the event, has been raised, and after the application calls the event handler, the application will continue to process the error normally. The Common Language Runtime (CLR) will also suspend thread aborts while the notification event is being processed, so the thread cannot be affected until after the handler has completed processing.
There are several ways to use first chance exception handling in your ASP.NET MVC application. The first is as a universal logging processor that standardizes logging efforts. However, you must be sure to handle the error. It might be appropriate to manage the work being done in the first chance exception handler through a configuration setting. This way you can control the risk of using first chance exception notification by enabling it only when needed.