Chapter 15. Tuning and Debugging Applications

Tuning and Debugging Applications

In this chapter, you’ll learn:

How to make your ASP.NET pages run more efficiently.

How to test and diagnose problems in ASP.NET pages.

All the examples and text so far in this book have emphasized getting Web pages to work in the simplest, most obvious way possible. This approach has many advantages in terms of learning time, development time, and ongoing maintenance time. However, as so often happens, the most obvious approach isn’t always the most efficient one. If everything goes right and your pages become a phenomenal success, you should expect to encounter a performance bottleneck or two.

ASP.NET pages are, of course, subject to all the performance considerations applicable to ordinary HTML pages: avoid transmitting large amounts of text, avoid transmitting large images, and so on. Beyond that are three further performance areas to consider: your program code, your usage of ASP.NET, and your database usage. This chapter offers specific recommendations to help improve your Web database performance.

At least a thousand and one things can go wrong with a given Web database page, and sooner or later you’re bound to experience every one of them. There’s a lot of software involved in processing a Web database page, and each component has its own requirements and eccentricities.

Neither this chapter nor any other in this book presents a detailed procedure for debugging any single Web database problem. At some point, you must understand how the software works, what your page is supposed to be doing, and how to make at least a shrewd guess about what an error message means. This chapter will, however, present a general approach for debugging and a brief description of Microsoft CLR Debugger, which provides interactive source-level debugging of ASP.NET pages.

Monitoring ASP.NET Application Performance

Only careful assessment can ensure that your application meets your visitors’ expectations. Assessment of a Web page’s performance typically involves one or more of these categories:

  • Throughput. The number of requests a Web application can process per unit of time, often measured in requests per second. Throughput can vary, depending on the load (number of client threads) applied to the server. This is usually the most important aspect of performance to optimize.

  • Response Time. The length of time between the last byte of a request and the first byte of the corresponding response. This is the aspect of performance visitors most often notice. If an application takes a long time to respond, the visitor might grow impatient and go to another site. The response time of an application can vary independently of (even inversely to) the rate of throughput.

  • Execution Time. The time it takes to process a request, usually measured between the first byte and the last byte the server returns to the client. Execution time directly affects throughput.

  • Scalability. An application’s ability to perform better as it receives more resources (that is, more memory, processors, computers, and so forth). A common scalability measurement compares the rate of throughput to the number of processors.

In order to write applications that perform well, you must understand and maintain the balance among these metrics. No single measurement can predict how your application will behave under all circumstances, but several measurements taken together can reveal how well your application performs under whatever circumstances you care to observe.

ASP.NET provides a number of tools that can help you test and monitor your Web application’s performance:

  • The Web Application Stress (WAS) tool simulates multiple visitors making requests to your Web site.

  • A number of Windows performance counters measure and report application performance in real time.

  • The built-in Trace feature reports execution statistics for a page or an application.

The WAS tool can control the client load, the number of connections, the format of cookies and HTTP response headers, and several other parameters from the tool’s graphical interface. After a test run, WAS can report performance metrics such as response time, throughput, and performance counter data for your application. This tool can help you maximize your application’s throughput and CPU use under high degrees of load. You can download WAS from homer.rte.microsoft.com. The WAS report in this graphic reports that the application is handling about 190 requests (hits) per second.

Scalability

Unlike traditional ASP technology, which exposes performance counters only for the entire server machine, ASP.NET exposes most performance counters on an application-by-application basis. To display these counters:

  1. Select Administrative Tools from the Start menu, and then select Performance.

  2. When the Performance window appears, click the Add toolbar button. (This is the one with a large plus sign on its face.)

  3. When the Add Counters dialog box appears, select the Use Local Computer Counters option if the application is running locally. Otherwise, select the Select Counters From Computer option and then type or select the name of the computer in the accompanying list box.

  4. In the Performance Object drop-down list, choose ASP.NET Applications.

  5. To display all counters for a given application, select the All Counters option. To display only specific counters, click the Select Counters From List option and then, in the list box just below, click the counters you want.

  6. Click the Add button to begin monitoring the selected counters.

  7. Click the Close button to close the Add Counters dialog box.

The following graphic shows these features in action.

Scalability

The ASP.NET Trace feature can show you important timing information between successive trace output statements, as well as information about the server control hierarchy, the amount of ViewState used, and the render size of controls on your page. To obtain a trace report, add the Trace="True" attribute to the page’s @ Page directive.

Performance is the opposite of entropy—it decreases all the time. Review performance regularly, at least by running your own Web pages and seeing whether they appear sluggish compared to a week or a month ago.

Achieving Performance Objectives

ASP.NET provides better performance than classic ASP, for three main reasons:

  • When ASP.NET receives the first request for a given page, it dynamically compiles and caches the code for that page. This step includes both compilation from source code to intermediate code and compilation from intermediate code to native code. Earlier versions of ASP interpreted code rather than compiling it, and did so in the order in which the code appeared on the page.

    The ASP.NET approach incurs a performance penalty when processing the first request, but provides a tremendous performance advantage while processing each additional request.

  • ASP.NET caches internal objects, such as server variables, to speed up access.

  • ASP.NET benefits from performance enhancements built into the common language runtime. These include not only just-in-time compiling, but also finely tuned use of both single and multiprocessor computers and many other enhancements.

As in any other programming environment, bad ASP.NET code can lead to poor performance. The best overall approach for tuning ASP.NET performance is to

  • Keep the ASP.NET page from doing any work that isn’t strictly necessary.

  • Keep ASP.NET pages from doing exactly the same work over and over again.

  • Avoid practices that are especially resource-intensive.

The next three sections provide suggestions and guidelines that will help your ASP.NET code perform at acceptable levels.

Tuning Your Program Code

Before diving headlong into tuning, make sure you’re tuning the right component. Measure before attempting to correct. Concentrate on the slowest, most frequently used Web pages. Web server activity logs will be helpful in this regard. Don’t try to solve database performance problems by optimizing ASP.NET code, or vice versa. The suggestions in this section pertain specifically to Visual Basic .NET.

Avoid retrieving the same data more than once

Loops are excellent places to look for code that executes repeatedly for no good reason. Consider, for example, the following code, which reads through the results of a query. For each record, it searches the lastname field to determine whether it contains a string named lastname received from the Web visitor.

Do While rdrMembers.Read
  If InStr(UCase(rdrMembers.Item("lastname")), _
                 UCase(Request.Params("lastname"))) Then
    booFound = True
    Exit Do
  Else
    booFound = False
  End If
Loop

If booFound is True when the loop ends, the Exit Do statement must have executed and the record is positioned at the first matching record. If booFound is False, there was no matching record.

More important, however, is that the loop evaluates the expression UCase(Reques.Params("lastname")) and sets the value of the booFound variable once for each record the data reader returns. If there are 50 records, that’s 50 searches through the Request.Params collection, plus 50 conversions to uppercase, plus 50 variable assignments. The following code, which performs the same operations only once, is much more efficient:

strLastname = UCase(Request.Params("lastname"))
booFound = False
Do While rdrMembers.Read
  If InStr(UCase(rdrMembers.Item("lastname")), _
                 strLastname) Then
    booFound = True
    Exit Do
  End If
Loop

Retrieving values from collections of any type is relatively slow. If you need to access such a value more than once, store a copy in a local variable.

Use Response.Write for string concatenation

Concatenating strings is a relatively resource-intensive operation. When you concatenate a 20-byte string and a 30-byte string, for example, the run-time code must allocate a new 50-byte area, copy all 50 bytes into it, adjust a pointer so that it points to the new area, and then release the original data area.

The Response.Write method, by contrast, offers very efficient buffering and concatenation services. Therefore, if you need to write several consecutive strings into the Response stream, coding a Response.Write statement for each one is more efficient that coding one Response.Write statement for a concatenation. That is,

Response.Write("Headline=")
Response.Write(strHeadline)
Response.Write(", Description=")
Response.Write(rdrAds.Item("Description"))

is more efficient than

Response.Write("Headline=") & _
               strHeadline & _
               ", Description=" & _
               rdrAds.Item("Description"))

Port call-intensive COM components to managed code

Although ASP.NET code can use traditional COM components, there are performance penalties in doing so. Each call to a COM component adds execution time and decreases throughput. The more calls your ASP.NET application makes to a given COM component, the greater the payback in converting that object to a new or existing .NET class. Alternatively, if the COM component is something you or your organization developed, you might consider redesigning it to require fewer calls or to pass more data during a single call.

Avoid redimensioning arrays

Visual Basic .NET has a ReDim statement that can change the size of an array. This command creates a new array of the requested size, optionally copies all the data from the old array, and then deletes the old array and replaces its address with that of the new one. This process makes redimensioning an array fairly time-consuming.

Never write loops that redimension an array once for each additional item. It’s much better to make the array fairly large to begin with and redimension it only in exceptional cases. If using a few extra kilobytes of memory presents a problem, it’s time to upgrade your hardware anyway.

Also, keep the ArrayList and Hashtable objects in mind. These objects are normally less efficient than an ordinary array, but more efficient than using an ordinary array poorly.

Don’t rely on exceptions in your code

Exception handling is an extremely time-consuming process. Therefore, you shouldn’t use a Try...Catch...End Try block to detect conditions that you could detect with normal code. Here are some examples:

  • Null values in a database field.

  • String values that standard methods can’t convert into a numeric value.

  • Specific values that upset math operations (such as divide by zero).

The following code, for example, is very inefficient:

Try
  lngQuotient = 100 / lngDivisor
Catch (e As Exception)
  lngQuotient = 0
End Try

The next block of code is much more efficient and produces the same result:

If lngDivisor = 0 Then
  lngQuotient = 0
Else
  lngQuotient = 100 / lngDivisor
End If

Use early binding

For years, Visual Basic, VBScript, and JScript programmers have used a feature called automatic type conversion. Under this concept, if a receiving variable wasn’t the correct type at run time, the language converted it. If the receiving variable didn’t exist, the language created it.

Visual Basic .NET calls this feature late binding and officially discourages it. Compared to early binding, where the compiler knows the one and only data type each variable will have, late binding is considerably less efficient.

There are two distinct ways to save yourself from the evils of late binding. The first is to make a habit of always declaring variables with fixed data types. In other words, don’t declare variables As Object or with no As clause at all.

The second way to prevent late binding is to code Option="Strict" in the @ Page directive for a Web page or the @ Control directive for a user control. This option will also prevent data conversions that might lose data, such as assigning a Long value to an Integer variable.

Tuning ASP.NET Usage

Extravagant use of ASP.NET features is another potential source of poor performance. Although for the most part ASP.NET does its job quite efficiently, there’s no point in activating features and then never using them.

Be sure to disable debug and trace modes

Always disable debug mode and trace mode before deploying a production application or conducting any performance measurements. If you leave these modes enabled, performance can suffer a great deal.

Preload the Application object with useful data

If you have Web pages that run frequently and use resource-intensive methods to obtain relatively static data, you can probably improve performance by retrieving that data just once and storing it in the Application object.

You can store almost anything you like in the Application object: simple variables, application parameters, arrays, and objects—to name just a few things. Remember, though, that everything in the Application object remains constantly in memory. Putting a few kilobytes of information into the Application object shouldn’t present a problem, but storing information by the megabyte is a bad idea.

Use Page.IsPostback to avoid unnecessary processing

When handling a Web form, the code that handles initial form display is usually quite different from the code that handles a submission (that is, a postback). During initialization, there’s no point in trying to process form data because at this point, the visitor hasn’t submitted any. There’s usually no reason to run the initialization code when an actual form submission (that is, a postback) occurs. The ViewState mechanism will preserve any form elements you initialize.

The Page.IsPostBack property makes it quite easy to differentiate these two cases. Here’s an example:

Sub Page_Load(sender As Object, e As EventArgs)
  If Page.IsPostBack
'  Code to process form submission goes here.
Else
'  Code to initialize form goes here.
  End If
End Sub

Use server controls appropriately

Each tag you code with the runat="server" attribute creates extra work for the Web server. Therefore, don’t code tags with this attribute unless you need to manipulate the control’s properties, handle the control’s events on the server, or take advantage of ViewState preservation. In many cases, a simple code render block or data-binding expression will serve the same purpose as a server control but use fewer resources.

Save the ViewState only when necessary

ASP.NET ViewState management saves the properties of server controls from one page execution to the next. ASP.NET does this by encoding the property values in a hidden form field that it adds to each outgoing response. When the visitor submits another request, ASP.NET decodes the hidden form field and restores the server control properties. By default, this process occurs for every server control on an ASP.NET page.

Of course, the more server controls you include in the ViewState and the more data they contain, the greater the load on the Web server. And, just to make life worse, ViewState preservation can sometimes be a nuisance. In the case of a Literal control that displays an error message, for example, you’d most likely prefer that ASP.NET not keep repeating it on every page (unless, of course, you remember to clear the message in code).

To disable the ViewState for a particular control, set its EnableViewState property to False, as in the following example:

<asp:datagrid EnableViewState="false" runat="server"/>

To disable the ViewState for an entire page or user control, set the EnableViewState property in the @ Page or @ Control directive. Here are some examples:

<%@ Page EnableViewState="false" %>
<%@ Control EnableViewState="false" %>

The standard trace display reports the amount of ViewState space each server control uses. To get this information, first enable tracing (by including a trace="true" attribute in the @ Page directive), run the page, and then look at the Viewstate column of the Control Hierarchy table.

Disable the session state when you aren’t using it

Not all applications or pages require the use of the Session object. On those that don’t, you might as well disable it. To disable the session state for a page, set the EnableSessionState attribute in the @ Page directive to False. For example,

<%@ Page EnableSessionState="false" %>

If a page requires access to session variables, but won’t create or modify them, set the EnableSessionState attribute in the @ Page directive to ReadOnly. To disable the session state for an entire application, open the application’s web.config file and then, in the sessionstate configuration section, set the mode attribute to off. Here’s an example:

<sessionstate mode="off" />

Choose your session state provider carefully

ASP.NET provides three distinct ways to store session data for your application:

  • In-process session state. This is the default.

  • Out-of-process session state as a Windows service.

  • Out-of-process session state in a SQL Server database.

Each approach has its advantages, but in-process session state is by far the fastest solution. If you’re storing only small amounts of volatile data in session state, use the in-process provider.

The two out-of-process solutions are useful primarily if you scale your application across multiple servers, or if you must preserve session data even though you restart a server or process.

Cache data and page output whenever possible

In cases where an ASP.NET page doesn’t need to generate fresh page output or data for every page request, caching can offer tremendous performance improvements. ASP.NET can even cache different versions of page output based on different incoming form field values. For more information about output caching, refer to "Coding the @ OutputCache Directive" in Chapter 3.

Avoid unnecessary round trips to the server

ASP.NET makes it easy for code on the Web server to respond to events on the browser. However, round trips are a trick best used sparingly. In general, you should initiate round trips only when your application needs to store or retrieve data. Interactive data display and data manipulation occur more smoothly and more efficiently on the browser.

Input validation is an excellent function to run on the browser. The Web visitor gets a faster response and the Web server gets fewer hits. Keep in mind, however, that some very old browsers don’t support browser-side scripting, and some current browsers let Web visitors turn it off. Don’t reduce server-side validation so much that outdated browsers or mischievous visitors can compromise your system.

Tuning Database Usage

Database design and usage are generally greater performance factors than application design and coding. In practical terms, this means that tuning database usage generally provides greater payback than optimizing your application code. The best approach varies depending on the situation, but if other ASP.NET pages on your server run fine and only the database pages are slow, it’s definitely time to do some database tuning.

Design your database for efficiency

If you followed the recommendations under "Designing a Database" in Chapter 6, your database should have a sound fundamental design. If you didn’t and now you have a mess, consider redesigning the database with all-new table names and then creating stored queries that mimic the tables in the original database. This will keep your existing Web site running while you revise the high-hitting, long-running Web pages to use the new structure. Here are some additional tips to keep in mind:

  • Eliminate redundant data. If you find yourself writing loops to update the same data in multiple records, it’s definitely time to review your database design.

  • Don’t make fields longer than necessary. This wastes space and increases physical disk activity.

  • Use the proper data type for each field. If the data consists of dates or times, use the Date/Time field format. If the field consists of integer values, don’t store them as floating-point or currency. Using the proper data types makes life easier for both you and the database system.

  • Anticipate and support common access paths. Real-world databases aren’t designed in ivory towers. Be prepared to sacrifice a bit of theoretical purity for real-world performance.

Optimize your queries

Poorly constructed queries are among the most common causes of poor database performance. Here are some tips for getting the same results in significantly less time:

  • Request no more data than necessary. If you don’t need a field, don’t specify it in a SELECT statement. If you’re filling a DataSet from a DataAdapter and don’t need all the records, set the MaxRecords property of the DataSet equal to the number you need. If you don’t need data from a joined table, delete the JOIN statement.

  • Given a choice between using WHERE and using HAVING, choose WHERE. Record selection specified on the WHERE clause occurs earlier in the retrieval process and speeds up overall database response.

  • Make the database system do as much of the work as possible. In general, you’re much better off telling the database system what you want than getting more data than you need and filtering or summarizing it yourself.

  • Avoid large result sets.

  • Avoid using criteria that require the database system to scan large numbers of records sequentially. Criteria involving LIKE, IN, NOT IN, OR, and != (not equal) are examples of these.

  • Column functions such as SUM are also resource-intensive, though generally less so than retrieving a large result set and performing the summation yourself. Use WHERE criteria to minimize the data processed by SUM or GROUP BY clauses.

Use SQL Server for data access

Of all the data access methods the .NET Framework provides, Microsoft highly recommends SQL Server for building high-performance, scalable Web applications. The .NET classes that access SQL Server use SQL Server’s native network data-transfer format to read data directly from a database connection, making it faster than the multi-layered architecture of OLE DB.

Use the DataReader classes for a fast forward-only data cursor

The DataReader classes retrieve a forward-only stream of data from a database, which is less functional but also more efficient than using a DataSet class. If a data reader meets your requirements, a data reader should be your solution of choice.

Don’t forget that data readers also implement the IEnumerable interface. You can bind data readers to Web server controls like the DataGrid and the DropDownList.

Use stored procedures and stored queries

Stored procedures are blocks of script code that reside on the database server. Microsoft SQL Server, for example, supports stored procedures written in a language called Transact-SQL. Because stored procedures run in an environment closely bound to the database server, they’re frequently the most efficient way to complete a series of database operations that depend on each other. When you want to execute a stored query, you just send the database system its name and any replaceable parameters. The topic "Using Stored Procedures" in Chapter 7 explained how to run a stored query.

Query optimization is one of the first steps required to process any SQL statement. During this step, the database system analyzes the submitted SQL, analyzes the structure of the database, and develops (with any luck) an optimal strategy for efficiently retrieving the requested data. Most databases perform this step when you save a stored query—not when an application runs it. With a conventional query, of course, the database system must optimize the query at run time. For this reason, running a stored query is often faster than running the same query supplied as text.

Avoid storing binary data in the database

Although Microsoft Access, SQL Server, and most other databases can store binary objects such as pictures and sounds, it’s best to avoid this practice for Web database pages. For one thing, these objects make the database physically larger and thus increase search times. More importantly, though, retrieving data stored this way requires querying the database an additional time for each object you use on the same Web page.

To understand this problem, consider that an ASP.NET page can’t send the browser an image or a sound file along with the HTML. It can only send an <img> tag that gives that file’s location. Thus, here’s how displaying a picture stored in a database has to work:

  1. The requested ASP.NET page sends the browser an <img> tag that gives the src= attribute as another ASP.NET page.

  2. When the browser receives this <img> tag, it dutifully requests that ASP.NET page.

  3. The second ASP.NET page looks up the record specified in the query string, gets the picture data, and writes it into the Response object. This page will also override the Response.ContentType property to audio/wav, image/gif, image/jpeg, or whatever is appropriate.

It should be obvious at this point that running another ASP.NET page, executing another query, and copying the data out of the database and into the Response object requires much more processing than delivering a simple .wav, .gif, or .jpg file. The best approach, therefore, is to store the picture, sound, or other binary file as a normal file on the Web server and record only its name in the database.

Create useful indexes

In the context of databases, an index is a table of values that exist within another table. However, the sequence of records in an index is different from the sequence of records in the original table. To see how this works, imagine that your database has an employee table with the employee number field as its primary key.

  • The database system would automatically create a primary key index that contained the employee number and physical location of each record in the employee table.

  • Pointers within the primary key index would significantly speed up reading or searching the index in employee number order, making it much faster than sorting or searching the employee table directly.

  • Whenever an application added, changed, or deleted a record in the employee table, the database system would automatically update the primary key index accordingly.

When you ask the database system to process a SQL statement, the query optimizer takes note of any applicable indexes and uses them to speed up processing. If the query asks for a specific primary key, for example, the optimizer will no doubt decide to search for it using the primary key index. Most database systems can support multiple indexes per table.

If you queried the same table for all employee surnames beginning with the letter B, the database system would have to read through every record in the employee table looking for surnames that begin with that letter. If you asked for these records in surname sequence, the database system would construct a temporary index of the found records and use it to retrieve the matching records in the requested order. Creating a temporary index involves much more work than using an existing index to do the same thing.

Although indexes speed lookup and often eliminate the need for sorting, they aren’t free. For one thing, indexes consume additional disk space. For another, whenever an application adds, modifies, or deletes a record in an indexed table, the database system also needs to update all the indexes. This process can be a significant performance hit.

In general, indexing a given field will be more attractive to the extent that the following conditions are true:

  • The table that contains the field is large.

  • The field is frequently used for selecting, joining, and sorting.

  • Record creations, record deletions, and updates to the specific field are infrequent.

However, there’s no hard-and-fast rule: you should be willing to create or delete indexes and then monitor the resulting performance.

General Debugging Tips

This section presents a number of tips and suggestions for getting Web database pages to work. To learn the cause of specific errors, type the error number or message into the Web page at www.microsoft.com/search and look for a match. You’ll often find good information at the MSDN Web site, the Microsoft Internet Information Services (IIS) Web site, or in the Microsoft Knowledge Base.

Verify the ASP.NET environment

If running a simple ASP.NET page such as the self-diagnosis page from Chapter 2 fails, your Web server isn’t configured to run ASP.NET pages. ASP.NET pages can run only on a Web server running Microsoft Internet Information Services, and only if they reside in a folder tree that has Script Execute permissions. You can’t, for example, load an ASP.NET page directly from your hard disk into your browser, as you might when previewing an ordinary Web page. Similarly, you can’t run ASP.NET pages by switching to Preview mode in Microsoft FrontPage. These modes work by storing and displaying the Web page temporarily on disk and not by processing the page on a Web server.

For information on configuring folders to be executable, refer to "Creating Executable Folders" in Chapter 2.

Test early and test often

Saving and running an ASP.NET page is such a rapid operation that you might as well test each part of your code as soon as you write it. If you test successfully, code another small function, and then test again, there’s little doubt where any problems lie.

Conversely, if you’re debugging some code that’s already grown large, try commenting out the code one logical function at a time (beginning, of course, from the page’s innermost workings). When the problem goes away, the last thing you commented out is probably its source.

Write trace messages

If you need to know the value of a certain variable or property, it’s often helpful to display it in a message you write to the Trace object. The following statement, for example, displays the value of a variable named strDescription:

Trace.Write("Description is '" & strDescription & "'.")

To view the message, you must specify Trace="True" in the @ Page directive. The great thing about this facility is that you don’t have to remove Trace.Write statements when you cut the page to production. Setting Trace="False" or removing the Trace attribute completely keeps these messages away from normal Web visitors.

The Trace.Warn method also writes messages into the trace log. The difference between Trace.Write and Trace.Warn is that Trace.Warn output appears in red.

Write event log messages

With very little trouble, ASP.NET pages can write to the Windows event log. Here’s some code that does this. The statements in blue apply specifically to using the event log.

<%@ Import Namespace="System.Diagnostics"%>
<script language="vb" runat="server">
Dim evtLog As New EventLog("Application", ".", "WebDbPgm")
Sub Page_Load(sender as Object, e as EventArgs)
  evtLog.WriteEntry("Writing to event log.")
End Sub
</script>

The three arguments in the Dim evtLog As New EventLog statement specify the log name, the machine name, and the event source. The log name is usually Application, Security, or System. A machine name of a single period (.) means the local machine. The event source identifies the source of the message in various event log displays.

Note

Event sources have a persistent identity on the computer that hosts the log. If you specify an event source that doesn’t already exist, Windows, security permitting, will create it on the fly. If you find that you lack permission to do this, contact an administrator.

To view event log messages, open the logs on the local machine by choosing Start, Programs, Administrative Tools, Event Viewer. To view logs on other computers, right-click Event Viewer in the Tree window and choose Connect To Another Computer from the shortcut menu. To view recent messages, you might need to press F5 or right-click the display and choose Refresh.

Debugging with Microsoft CLR Debugger

The .NET Framework includes an application called Microsoft CLR Debugger that provides source-level, interactive debugging of ASP.NET Web pages. Using CLR Debugger, you can view your code as its runs, halt it for inspection, execute a line at a time, set break points, inspect and modify variables, inspect and modify object properties, and so forth, all through an integrated graphical user interface.

CLR Debugger has no remote debugging capability. To interactively debug an ASP.NET page, you must run the debugger and the Web server on the same computer. In addition, any page you debug must have the Debug="True" option in effect. You can specify this option in either of two ways:

  • You can specify Debug="True" in the @ Page directive for an individual page.

  • You can add the line shown here in blue to the web.config file for the application:

    <configuration>
      <system.web>
        <compilation debug="true" />
      <system.web>
    </configuration>

Debug an ASP.NET page interactively

Here’s the procedure for interactively debugging an ASP.NET page:

  1. Log on to the console of the machine running the Web server. Be aware that whenever you stop execution of a page, the Web server might stop servicing other requests. For this reason, if no other, you should always debug on a test Web server—perhaps one running on Windows XP Professional.

  2. Locate the debugger program DbgCLR.exe in Windows Explorer and double-click it. By default, this program resides in the folder C:Program FilesMicrosoft.NETFrameworkSDKGuiDebug. (If you debug frequently, you might want to right-drag this program to your desktop and create a shortcut.)

  3. When the Microsoft CLR Debugger window appears, choose Debug Processes from the Tools menu.

  4. When the Processes window appears, select the Show System Processes check box.

  5. In the Available Processes list, select aspnet_wp.exe. If this process doesn’t appear, start your browser and then request any ASP.NET page from the Web server. Then go back to the Processes window and click the Refresh button.

    Once you’re selected aspnet_wp.exe, click the Attach button and then click the Close button. The graphic on the next page shows the aspnet_wp.exe process selected.

    Debug an ASP.NET page interactively
  6. Back in the Microsoft CLR Debugger window, choose Open from the File menu and then File from the resulting submenu. Then, when the Open File dialog box appears, select the file you want to debug and then click Open.

    Note that the file you open must be the physical file that corresponds to the page’s URL. This will generally be a file within the C:InetPubwwwroot folder tree.

  7. If the page is throwing an exception and you want to stop at the offending statement, proceed to step 8. Otherwise, find the line of code where you want execution to stop, select it, and then press F9. The debugger will display a red circle in the left margin near this line of code, indicating that a so-called breakpoint exists at that line.

  8. Start your browser and request the page you want to debug. When execution reaches the statement where the exception or breakpoint occurs, the debugger will pause the program and highlight that statement with a yellow background.

  9. To inspect the values of any variable, property, or object, locate its name in the Locals pane. The corresponding value will appear in the Value column, and the data type will appear in the Type column. If the value appears in red, it means the value changed as a result of executing the previous statement. To change a value, select it and then type the new value.

    If the Locals pane doesn’t appear, choose Windows from the Debug window and then choose Locals.

  10. To enter and immediately execute any Visual Basic .NET statement, type it into the Immediate window and press Enter. If a given statement isn’t producing the results you want, for example, you could try entering variations in the Immediate window until you find one that works. To display the value of a variable or expression, type a question mark, a space, and then the variable name or expression.

  11. To resume execution, choose one of the following commands from the Debug menu:

    • Continue (F5) initiates or resumes execution of the selected page.

    • Stop Debugging (Shift+F5) closes the debugging session on the currently selected page.

    • Step Into (F11) executes the current statement and then halts. If the current statement calls another procedure, the debugger will enter that procedure and pause at the first statement.

    • Step Over (F10) executes the current statement and then halts. If the current statement calls another procedure, the debugger runs the entire procedure and pauses at the first statement after the current statement.

    • Step Out (Shift+F11) executes all remaining statements in the current procedure and pauses just after the statement that called it.

  12. To stop the debugger, choose Exit from the File menu.

    In this graphic, execution halted at the statement If Page.IsPostBack Then. The developer then pressed the F11 key once and looked up the value of the txtFind.Text property.

    Debug an ASP.NET page interactively

Summary

This chapter provided a series of tips for improving the performance of Web database pages. It centered primarily on three areas: Visual Basic .NET performance, ASP.NET performance, and database performance. It also presented a number of tools and techniques for testing, diagnosing, and correcting Web database pages. It concluded with a brief introduction to Microsoft CLR Debugger, which provides interactive debugging of browser-side and server-side script code.

This book has explained the fundamentals of creating database applications that clients access via the World Wide Web. Its approach has been practical rather than theoretical, compact rather than broad. I hope this approach has been useful, and that you’re now prepared to create applications of your own. Best wishes and good luck with your site.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.135.36