Before ASP 3.0, error handling was never a strong suit of ASP. Despite taking great efforts to handle possible error conditions, it is not uncommon to see ASP applications crash and display cryptic error messages. For applications critical to a company’s success, this is a huge embarrassment. You may have seen something like this quite often:
Besides handling errors, how many times have you forgotten to remove debugging statements in your application? Often, due to the unrealistic and tight deadlines imposed by management, you end up rushing to deploy your application. In the midst of doing that, a few debugging statements occasionally get left out.
In this chapter, we will look at the new error handling mechanisms available in .NET. We will discuss how to anticipate various kinds of errors and their possible remedies. We will also look at how the new Trace class in ASP.NET allows programmers to trace the flow of ASP.NET applications as well as explore the various capabilities available in the Trace class. Finally, we will show you how to use the Object browser and Class Viewer to look for specific libraries.
While it is the hope of every programmer to write bug-free programs, it can prove a tasking goal. Bugs in programs can be incredibly frustrating, usually disrupting the programs they infect. Such errors can be classified into these four categories, which we’ll discuss in the following sections:
A syntax error is one of the most common errors in programming. This is especially true if you are new to a particular language. Fortunately, syntax errors can be resolved quite easily. In Visual Studio .NET, syntax errors are underlined as shown in Figure 9.1.
To know the cause of the error, simply position the mouse over the underlined word and the tool tip box will appear. The cause of the error in Figure 9.1 is the misspelled word “Integer.” To correct the error, simply change the word “Interger” to “Integer.”
Compilation errors occur when the compiler tries to compile the program and realizes that the program contains codes that may potentially trip up a program. As an illustration, consider the following example, which declares two variables of different data types:
The last line of the code tries to assign the value of an Int32 variable to another variable of type Int16. The risk here is that during runtime, intNum might contain a value that is larger than the range represented by the Short data type. Hence this assignment is not safe (although it will compile and may run without error). This form of assignment where the value of a “wider” data type is assigned to a variable of a “narrower” data type is known as narrowing. The reverse is known as widening. Narrowing is dangerous and could possibly result in runtime errors.
The Option Strict On statement must be placed at the first line of your program. Using the Option Strict On also implies Option Explicit On (the Option Explicit statement ensures that variables are declared prior to usage). Thus undeclared variables would also generate error messages.
Runtime errors occur during the time when the application is running and something unexpected occurs. It happens regularly in projects that have very tight deadlines. Programmers stretched to their limits are often satisfied that their program runs. They do not have the time to carefully consider all the different possible scenarios in which their programs may be used, hence the result is often a buggy program. To ensure that an application is as robust and bug-free as possible, it is important to place emphasis on anticipating all the errors that can occur in your program.
Error handling got a new lease on life in the .NET Framework, particularly within the .NET languages. In VB6, error handling was unstructured, done using the primitive On Error statement. In the .NET languages, specifically in VB.NET, error handling can both be structured and unstructured. We will examine the two modes of handling errors in the next section.
You should see the error as shown in Figure 9.2.
The On Error Resume Next statement ignores any error that happens and continues as though no error has occurred. The error information is contained within the Err object. If an error has occurred, the property Number of the Err object would contain a nonzero value. The Description property will contain the description of the error. Some common errors and their descriptions are shown in Table 9.1.
|On Error Statement||Description|
|On Error Resume Next||Specifies that in the event an error occurs, resume execution.|
|On Error Goto −1||Disables enabled exception in the current subroutine and resets it to Nothing.|
|On Error Goto 0||Disables error handling.|
|On Error Goto label||Specifies the location to jump to when an error occurs.|
In the preceding example, we examine three errors. The first error will cause the execution to jump to the ErrorHandling block and after the error description has been printed, it resumes execution at the point it was interrupted. The second error will be ignored while the third error will cause the program to fail.
As you can see, unstructured error handling makes your code messy and difficult to debug, and also affects future maintenance. Hence, the recommended way to handle errors is to use structured error handling, which is covered in the next section.
Using unstructured error handling usually results in messy and difficult-to-maintain codes. Rather than placing an On Error statement at the beginning of a block to handle potential errors, .NET supports structured error handling using the Try-Catch-Finally construct. Structured error handling uses the Try-Catch-Finally construct to handle exceptions. The Try-Catch-Finally construct allows developers to actively “catch” different forms of errors and respond to them appropriately. It has the following syntax:
Here we have multiple Catch statements. Each Catch statement tries to catch the different kinds of exceptions. If discovered, the exception is evaluated from top to bottom. Once a match is found, the codes within the Catch block are executed. If no match is found, an error message is displayed.
When the statement within the Try block generates an exception, the few Catch statements are evaluated in order. First, it compares with the initial Catch block and checks to see if it matches the kind of exception specified in the Catch statement. If it doesn’t, it will compare it with the next, and so on. It only stops when a match is found. In our case, the exception is an overflow exception and hence the second Catch block is matched. If no match is found, an error message will be generated.
Logic errors are the most difficult problem to solve! While the previous errors can be taken care of with the help of special language constructs and the compilers, logic errors cannot be resolved so easily. Logic errors result when a piece of code does not work as intended. As an example, consider the following code snippets:
The code is trying to calculate the factorial of a number. Though there are no syntax errors, the code is not producing the expected answer (120). In fact, no result is printed. Only through some tracing and checking is it found that the culprit is actually forgetting to initialize the value of the factorial. To facilitate the debugging of logic errors, ASP.NET provides tracing ability. We will elaborate on the tracing feature available in ASP.NET in the next section.
During the development stage, you may often need to monitor the value of some variables or functions, especially if they are not giving the correct results. Tracing through the codes is another important debugging method to make sure your codes flow in the intended manner.
To activate the trace, the page directive needs to have a Trace attribute with its value set to “true,” as shown in Figure 9.3. By just changing the value of the Trace attribute, we can turn tracing on or off. When the application is ready for deployment, simply set the Trace attribute to the value “false.” There is no need to remove the Trace statements in your application.
|Request Details||Describes information pertaining to the request (e.g., SessionID, Encoding, and time of request).|
|Trace Information||Contains detailed information about the application currently running. Trace information is displayed in this section.|
|Control Tree||Displays information about controls used in a page and the size of the Viewstate hidden field.|
|Cookies Collection||Displays the cookie set by the page and its value.|
|Headers Collection||Displays HTTP header information like content length and user agent.|
|Forms Collection||Displays the name of controls in a page and its value.|
|Server Variables||Displays the environment variables on the server side.|
|IsEnabled||Indicates whether tracing is enabled for the current request.|
|TraceMode||Sets the trace mode: sortByCategory or sortByTime.|
|Warn||Writes the trace information in red.|
|Write||Writes the trace information.|
For example, the Warn() method of the Trace class causes the Trace information to be printed in red as shown in Figure 9.5 (all the nonshaded lines you see are displayed in red).
Inserting multiple Trace statements in an application can sometimes be messy. It is useful if the Trace information is classified into different categories to make tracing easier. The Trace class allows us to create different debugging categories and sort the Trace information based on these categories. The following example shows how to group the different categories of Trace information:
The output of the preceding code is shown in Figure 9.6.
Since we are sorting the Trace mode by category, notice that Figure 9.6 shows the messages are sorted by category.
Although displaying the Trace information within the page is useful, sometimes you need to trace the page while users are utilizing your application. In such cases, the user should not see the Trace information.ASP.NET provides a mean for the Trace information to be written to a log file. The following example shows how the Trace information is written to the application log:
The System.Diagnostics namespace provides the class to debug our application. In particular, we used the EventLog component to help us write messages to the application log. To view the message, use the Event Viewer. Our message is shown in Figure 9.7.
To see the message details, double-click the event item. The detailed message is shown in Figure 9.8.
This last section discusses page tracing which maps the flow within a page. ASP.NET also supports tracing at the application level. Application-level tracing is set in the web.config file, under the trace section:
To enable an application-level trace, set the following values shown in Table 9.5.
|enabled||true||Enables or disables application-level tracing.|
|requestLimit||10||Sets the maximum number of requests to trace.|
|pageOutput||false||Displays the trace at the end of the page.|
|traceMode||SortByTime||Trace information sort order.|
|localOnly||true||Sets the ability to see trace viewer on a nonlocal machine.|
When the application is loaded, the Trace information does not appear on the page. To view the Trace information, we need to use the Trace viewer (trace.axd) shown in Figure 9.9.
Figure 9.9 shows the Trace information of the last ten requests to the application. To view the detailed information of each request, click the View Details link of each row.
Besides using the Trace class to trace the value of variables in your application, another method is to set breakpoints in your application. Visual Studio .NET allows you to do this so you can examine and trace the flow of your application during runtime. Figure 9.10 shows a breakpoint (indicated by a dot, which shows up as red on the screen).
Step Out This option is available if the current execution point is in the function and you want to execute the rest of the codes in the function without stepping through them. It then returns to the calling function.
During compilation, debugging symbols (.pdb information) are inserted into the compiled page. As a result, the application will run slower than without the debugging symbols. As such, remember to set the debug attribute to false when you deploy your application.
One of the key aspects of successful .NET programming is the ability to use the appropriate class libraries provided by the framework. While the MSDN documentation is a good place to find out about the class libraries, a better option would be to use the Object Browser provided by the .NET SDK. To launch the Object Browser in Visual Studio .NET, press Ctrl+Alt+J.
Figure 9.11 shows the Object Browser with the System assembly and its associated namespaces exposed. Members of the class UriFormationException are shown on the right window, while the bottom window shows the description of the selected member.
Besides employing the Object Browser to view the various class libraries available, you can also use the Class Viewer. To launch the Class Viewer, type WinCV at the command prompt. Figure 9.12 shows the Class Viewer.
The Class Viewer allows you to type in the keyword to search and display all matching instances of the search word. For example, Figure 9.12 shows the search result for “overflowexception.” It also displays the corresponding namespace and the members of the selected class.
Error handling is an important aspect of software development. Good robust applications anticipate various errors and take an active role in resolving them without crashing the program. In this chapter, we have seen two distinctive methods of error handling—structured and unstructured. While the unstructured error handling mechanism continues to be supported in .NET, it is recommended that programmers make the switch to the structured error handling mechanism using the Try-Catch-Finally statement. Besides handling errors, the new tracing capability found in .NET makes the life of a programmer much easier. No longer do you have to insert Response. Write statements into your application, you can now trace your application using the Trace class. Removing the Trace statements during deployment is simply a matter of setting an attribute. Finally, Visual Studio .NET allows you to set breakpoints in your application so that the flow of variables and codes can be examined during runtime.
Frequently Asked Questions
The following Frequently Asked Questions, answered by the authors of this book, are designed to both measure your understanding of the concepts presented in this chapter and to assist you with real-life implementation of these concepts. To have your questions about this chapter answered by the author, browse to www.syngress.com/solutions and click on the “Ask the Author” form.
Q: When I try to run my ASP.NET application in VS.NET, I encounter this error message “Error while trying to run project: Unable to start debugging on the Web server. The project is not configured to be debugged.” Why does this occur?
A: This is caused by the setting of the debug attribute within the <compilation> element. During development stage, set the value of the debug attribute to “true.” Remember, however, to set this attribute to “false” when you are ready to deploy your application.
A: For performance reasons, the .NET Framework does not maintain state between the Web server and the Web browser automatically, hence the Session ID is always different between submissions. However, when the Session object is used or when the Session_OnStart() event is added to the global.asax file, the Session ID would be maintained between postbacks.