Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1130
Chapter 24: Debugging and Error Handling
Error Notifications
During an interactive debugging session, Visual Studio now strives to assist you with informative Error
Notifications. These notifications not only report on events such as unhandled exceptions, but also offer
context-sensitive troubleshooting tips and next steps for dealing with the situation. Figure 24-15 shows
an unhandled
NullReferenceException
along with the good advice that we might try using the ‘‘new’’
keyword to create an object instance before using it. Oops!
Figure 24-15
Edit and Continue (Lack of) Support, or Edit and Refresh
Visual Basic 6 was all about developing things quickly, and its most powerful feature was the Edit and
Continue feature, which gave you capability to change code during a debugging session without restart-
ing the session. In break mode, you could modify code fix bugs and move on. The 2.0 version of the CLR
has restored this feature for both C# and Visual Basic. Although this has a large number of developers
cheering, unfortunately this feature is not available to ASP.NET developers.
In ASP.NET, your assembly is compiled not by Visual Studio, but by the ASP.NET runtime using the
same technique it does during a normal Web page request by a browser. To cooperate with the debugger
and support Edit and Continue within ASP.NET, a number of fantastically complex modifications t o
ASP.NET runtime would have been required by the development team. Rather than including support
for this feature, ASP.NET developers can use page recycling.
This means that code changes are made during a debugging session, and then the whole page is refreshed
via F5, automatically recompiled, and re-executed. Basically, ASP.NET 2.0 includes much improved
support for Edit and Refresh, but not for Edit and Continue.
Just My Code Debugging
A new concept in the .NET 2.0 CLR is called Just My Code debugging. Any method in code can be explicit-
ly marked with the new attribute
[DebuggerNonUserCode]
. Using this explicit technique and a number of
1130
Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1131
Chapter 24: Debugging and Error Handling
other heuristic methods internal to the CLR, the debugger silently skips over code that isn’t important to
the code at hand. You can find the new preference Enable Just My Code in Tools ➪ Options ➪ Debugging.
The
[DebuggerHidden]
attribute is still available in .NET 2.0 and hides methods from the debugger,
regardless of the user’s Just My Code preference. The 1.1 attribute
[DebuggerStepThrough]
tells the
debugger to step through, rather than into, any method to which it’s applied; the
[DebuggerNonUser
Code]
attribute is a much more pervasive and complete implementation that works at runtime on dele-
gates, virtual functions, and any arbitrarily complex code.
Be aware that these attributes and this new user option exist to help you debug code effectively and
not be fooled by any confusing call stacks. While these can be very useful, be sure not to use them on
your components until you’re sure you won’t accidentally hide the very error you’re trying to debug.
Typically these attributes are used for components such as proxies or thin shim layers.
Tracepoints
Breakpoints by themselves are useful for stopping execution either conditionally or unconditionally.
Standard breakpoints break always. Conditional breakpoints cause you to enter an interactive debug-
ging session based on a condition. Tracing is useful to output the value of a variable or assertion to the
debugger or to another location. If you combine all these features, what do you get? Tracepoints, a new
and powerful Visual Studio feature. Tracepoints can save you from hitting breakpoints dozens of times
just to catch an edge case variable value. They can save you from covering your code with breakpoints to
catch a strange case.
To insert a Tracepoint, right-click in the code editor and select Breakpoint ➪ Insert Tracepoint. You’ll get
the dialog shown in Figure 24-16. The icon that indicates a breakpoint is a red circle, and the icon for a
Tracepoint is a red diamond. Arbitrary strings can be created from the dialog using pseudo-variables
in the form of keywords such as
$CALLSTACK
or
$FUNCTION
,aswellasthevaluesofvariablesinscope
placed in curly braces. In Figure 24-16, the value of
i.FirstName
(placed in curly braces) is shown in the
complete string with the Debug output of Visual Studio.
Client-side Javascript Debugging
Excellent client-side Javascript Debugging is new in Visual Studio 2008. If you run an ASP.NET appli-
cation in a debugging session in Internet Explorer you’ll need to enable script debugging. If not, you’ll
receive a dialog similar to the one in Figure 24-17.
After you’ve turned on Script Debugging, try a simple ASPX page with some Javascript that changes the
text in a textbox to UPPERCASE when the button is pressed.
Listing 24-5: Simple Javascript debugging test
ASPX
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " />xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns=" >
<head runat="server">
<script type="text/javascript">
function MakeItUpper()
Continued
1131
Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1132
Chapter 24: Debugging and Error Handling
{
newText = document.getElementById("TextBox1").value.toUpperCase();
document.getElementById("TextBox1").value = newText;
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<input type="button" id="Button1" value="Upper"
onclick="javascript:MakeItUpper()" />
<input type="text" id="TextBox1" runat="server"/>
</div>
</form>
</body>
</html>
Figure 24-16
Put a breakpoint on one of the lines of client-side Javascript. Note that this is code that runs in the browser,
not on the Web server. Start a debugging session with the page from Listing 24-5. Visual Studio will break
at that point, as shown in Figure 24-18.
The Javascript debugger in Visual Studio 2008 supports variable tooltips, visualizers, call stacks, locals,
watches, and all the features you’re used to when debugging .NET-based languages.
1132
Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1133
Chapter 24: Debugging and Error Handling
Figure 24-17
Figure 24-18
1133
Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1134
Chapter 24: Debugging and Error Handling
Notice in the figure that
Default.aspx
has the word [dynamic] listed on the tab, indicating that this isn’t
the same
default.aspx
that was edited earlier; you can see that
default.aspx
listed on t he final tab.
Rather, this is the dynamically generated
default.aspx
that was delivered to the browser, including
ViewState and other generated elements. Dynamically generated documents and scripts appear during
the debug session in the Solution Explorer.
This rich debugging support on the client side makes creating today’s Javascript-heavy AJAX a pplica-
tions much easier.
SQL Stored Proc Debugging
Database projects are file-based projects that let you manage and execute database queries. You can add
your existing SQL scripts to the project or create new ones and edit them within Visual Studio. Database
projects and SQL debugging are not available in the Express or Standard versions of Visual Studio. They
are available only in the Professional or Team Edition Visual Studio SKUs/versions.
When debugging database applications, you can’t use Step Into (F11) to step between code in the appli-
cation tier into the code in SQL Server 2005 (be it T-SQL or CLR SQL). However, you can set a breakpoint
in the stored procedure code and use Continue (F5) to execute code to that set break point.
When debugging SQL on SQL Server 2005, be aware of any software or hardware firewalls you may be
running. Windows XP SP2’s software firewall will warn you what you’re trying to do. Be sure to select
‘‘unblock’’ in any warning dialogs to ensure that SQL Server 2005 and Visual Studio can communicate.
If you are using a SQL account to connect to the SQL Server, make sure the Windows User Account
you run Visual Studio under is also an administrator on the SQL Server machine. You can add accounts
to SQL Server’s
sysadmin
privilege using the SQL command
sp_addsrvrolemember ’Domain
\
Name’,
’sysadmin’
. Of course, never do this in production; and better yet, do your debugging on a machine
with everything installed locally.
If you’re using the NT Authentication model on the SQL Server 2005, make sure that account has per-
missions to run the
sp_enable_sql_debug
stored procedure. You can give account access to this stored
procedure by using the SQL commands
CREATE USER UserName FOR LOGIN ’Domain
\
Name’
followed by
GRANT EXECUTE ON sp_enable_sql_debug TO UserName
. This creates a SQL user that is associated directly
with a specific Windows User and then explicitly grants permissions to debug TSQL to that user. On SQL
Server 2000, the user must have access to the e xtended stored procedure
sp_sdidebug
.
For slightly older installations such as Windows 2000 and Windows NT 4, or if you are using SQL 2000,
be sure to visit MSDN for the latest details and tools in this space. The MSDN URL for debugging SQL
Server is
/>.
Exception and Error Handling
When an exception occurs in your ASP.NET application code, you can handle it in a number of ways, but
the best approach is a multi-pronged one:
❑ Catch what you expect:
❑ Use a
Try/Catch
around error-prone code. This can always catch specific exceptions that
you can deal with, such as
System.IO.FileNotFoundException
1134
Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1135
Chapter 24: Debugging and Error Handling
❑ Rather than catching exceptions around specific chunks of code at the page level, consider
using t he page-level error handler to catch specific exceptions that might happen anywhere
on the page.
❑ But prepare for unhandled exceptions:
❑ Set the
Page.Error
property if a specific page should show a specific error page for any
unhandled exception. This can also be done using the
<
%@ Page
> directive or the code
behind the property.
❑ Have default error pages for 400 and 500 errors set in your
web.config
.
❑ Have a boilerplate
Application_OnError
handler that takes into consideration both spe-
cific exceptions that you can do something about, as well as all unhandled exceptions that
you may want logged to either the event log, a text file, or other instrumentation
mechanism.
The phrase unhandled exception may be alarming, but remember that you don’t do anyone any good
catching an exception that you can’t recover from. Unhandled exceptions are okay if they are just that —
exceptional. For these situations, rely on global exception handlers for logging and friendly error pages
that you can present to the user.
Why try to catch an exception by adding code everywhere if you can catch and log
exceptions all in one place? A common mistake is creating a try/catch block around
some arbitrary code and catching the least specific exception type —
System.
Exception
. A rule of thumb is, don’t catch any exception that you can’t do anything
about. Just because an exception can be thrown by a particular method doesn’t
mean you have to catch it. It’s exceptional, remember? Also, there are exception
handlers at both the page and the application level. Catch exceptions in these two
centralized locations rather than all over.
Handling Exceptions on a Page
To handle exceptions at a page level, override the
OnError
method that
System.Web.UI.Page
inherits
from the
TemplateControl
class (see Listing 24-5). Calling
Server.GetLastError
gives you access to the
exception that just occurred. Be aware that a chain of exceptions may have occurred, and you can use the
ExceptionGetBaseException
method to return the root exception.
Listing 24-6: Page-level error handling
VB
Protected Overrides Sub OnError(ByVal e As System.EventArgs)
Dim AnError As System.Exception = Server.GetLastError()
If (TypeOf AnError.GetBaseException() Is SomeSpecificException) Then
Response.Write("Something bad happened!")
Response.StatusCode = 200
Server.ClearError()
Response.End()
End If
End Sub
Continued
1135
Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1136
Chapter 24: Debugging and Error Handling
C#
protected override void OnError(EventArgs e)
{
System.Exception anError = Server.GetLastError();
if (anError.GetBaseException() is SomeSpecificException)
{
Response.Write("Something bad happened!");
Response.StatusCode = 200;
Server.ClearError();
Response.End();
}
}
Handling Application Exceptions
The technique of catching exceptions in a centralized location can be applied to error handling at the
application level in
Global.asax
, as shown in Listing 24-6. If an exception is not caught on the page,
the
web.config
is checked for an alternate error page; if there isn’t one, the exception bubbles up to the
application and your user sees a complete call stack.
Listing 24-7: Application-level error handling
VB
Protected Sub Application_Error(sender as Object, ByVal e As System.EventArgs)
Dim bigError As System.Exception = Server.GetLastError()
’Example checking for HttpRequestValidationException
If (TypeOf bigError.GetBaseException() Is HttpRequestValidationException) Then
System.Diagnostics.Trace.WriteLine(bigError.ToString)
Server.ClearError()
End If
End Sub
C#
protected void Application_Error(Object sender, EventArgs e)
{
System.Exception bigError = Server.GetLastError();
//Example checking for HttpRequestValidationException
if(bigError.GetBaseException() is HttpRequestValidationException )
{
System.Diagnostics.Trace.WriteLine(bigError.ToString());
Server.ClearError();
}
}
Unhandled application errors turn into HTTP Status Code 500 and display errors in the browser. These
errors, including the complete callstack and other technical details, may be useful during development,
but are hardly useful at production time. Most often, you want to create an error handler (as shown
previously) to log your error and to give the user a friendlier page to view.
1136
Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1137
Chapter 24: Debugging and Error Handling
If you ever find yourself trying to catch exceptions of type
System.Exception
,takea
look at the code to see whether you can avoid it. There’s almost never a reason to
catch such a non-specific exception, and you’re more likely to swallow exceptions
that can provide valuable debugging. Check the API documentation for the
framework method you are calling — a section specifically lists what exceptions an
API call might throw. Never rely on an exception occurring to get a standard code
path to work.
Http Status Codes
Every
HttpRequest
results in an
HttpResponse
, and every
HttpResponse
includes a status code. The
following table describes 11 particularly interesting HTTP status codes.
Status Code Explanation
200 OK Everything went well.
301 Moved
Permanently
Reminds the caller to use a new, permanent URL rather than the one he used to
get here.
302 Found Returned during a Response.Redirect. This is the way to say ‘‘No, no, look over
here right now.’’
304 Not
Modified
Returned as the result of a conditional
GET
when a requested document hasn’t
been modified. It is the basis of all browser-based caching. An HTTP
message-body must not be returned when using a 304.
307
Temporary
Redirect
Redirects calls to ASMX Web services to alternate URLs. Rarely used with
ASP.NET.
400 Bad
Request
Request was malformed.
401
Unauthorized
Request requires authentication from the user.
403 Forbidden Authentication has failed, indicating that the server understood the requests but
cannot fulfill it.
404 Not Found The server has not found an appropriate file or handler to handle this request.
The implication is that this may be a temporary state. This happens in ASP.NET
not only because a file cannot be found, but also because it may be
inappropriately mapped to an IHttpHandler that was not available to service
the request.
410 Gone The equivalent of a permanent 404 indicating to the client that it should delete
any references to this link if possible. 404s usually indicate that the server does
not know whether the condition is permanent.
1137
Evjen c24.tex V2 - 01/28/2008 3:40pm Page 1138
Chapter 24: Debugging and Error Handling
Status Code Explanation
500 Internal
Server Error
The official text for this error is ‘‘The server encountered an unexpected
condition which prevented it from fulfilling the request,’’ but this error can occur
when any unhandled exception bubbles all the way up to the user from
ASP.NET.
Any status code greater than or equal to 400 is considered an error and, unless you configure otherwise,
the user will likely see an unfriendly message in his browser. If you have not already handled these
errors inside of the ASP.NET runtime by checking their exception types, or if the error occurred outside
of ASP.NET and you want to show the user a friendly message, you can assign pages to any status code
within
web.config
, as the following example shows:
<customErrors mode ="On" >
<error statusCode ="500" redirect ="FriendlyMassiveError.aspx" />
</customErrors>
After making a change to the customer errors section of your
web.config
, make sure a page is available
to be shown to the user. A classic mistake in error redirection is redirecting the user to a page that will
cause an error, thereby getting him stuck in a loop. Use a great deal of care if you have complicated
headers or footers in your application that might cause an error if they appear on an error page. Avoid
hitting the database or performing any other backend operation that requires either user authorization or
that the user’s session be in any specific state. In other words, make sure that the error page is a reliable
standalone.
Any status code greater than or equal to 400 increments the ASP.NET Requests
Failed performance counter. 401 increments Requests Failed and Requests Not
Authorized. 404 and 414 increment both Requests Failed and Requests Not Found.
Requests that result in a 500 status code increment Requests Failed and Requests
Timed Out. If you’re going to return status codes, you must realize their effects and
their implications.
Summary
This chapter examined the debugging tools available to you for creating robust ASP.NET applications.
A successful debugging experience includes not only interactive debugging with new features such as
datatips, data visualizers, and error notifications, but also powerful options around configurable tracing
and logging of information.
Remote debugging is easier than ever with ASP.NET, and the capability to write and debug ASP.NET
pages without installing IIS removes yet another layer of complexity from the development process.
Visual Studio and its extensible debugging mechanisms continue to be expanded by intrepid bloggers
and enthusiasts, making debugging even less tedious than it has been in the past.
1138
Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1139
File I/O and Streams
Although most of this book concentrates specifically on learning and using the features of ASP.NET
3.5, .NET provides an enormous amount of additional functionality in other areas of the Base Class
Library (BCL). This chapter examines a few of the common base classes that you can use to enhance
your ASP.NET applications. First, you look at using the frameworks
System.IO
namespace to man-
age files on the local file system. Next, you explore how to use the various Stream classes within
the framework to read from and write different data formats to memory and the local file system.
Finally, you learn how to use the .NET Framework to communicate with other computers across
the Internet using common protocols such as HTTP and FTP.
A Word about I/O Security
Although this chapter is not specifically about ASP.NET security, you need to
understand the impact of local system security on what the ASP.NET Worker Process
is allowed to do inside of the IO namespace. Remember that generally, when your code
is executed by IIS, it executes under the context of the ASP.NET Worker Process user
account (ASPNET) and, therefore, your application may be restricted by that account’s
security rights. For example, by default, the ASP.NET Worker Process does not have
rights to write to the local disk. The two main areas that you should look at to get a
very basic understanding of the impact of security on an application are impersonation
and user account ACLs. ASP.NET security is discussed thoroughly in Chapter 18.
Additionally, this chapter demonstrates how to use classes in the BCL to delete files
and directories and to modify the permissions of directories and files. Recognize that
it is entirely possible to permanently delete important data from your hard drive or
change the permissions of a resource, which would result in you losing the ability to
access the resource. Be very careful when using these classes against the file system.