Chapter 16 Caching Output 361
{
Thread.Sleep(0);
Response.Write("This page was generated and cached at: " +
DateTime.Now.ToString());
Response.Cache.SetNoServerCaching();
Response.Cache.SetLastModified(DateTime.Now);
}
}
Caching Locations
In addition to varying the number of cached versions of a page, you may tell ASP.NET
where to cache the content. This is controlled through either the Location attribute in the
OutputCache directive or by using the HttpCachePolicy class’s SetCacheability method.
ASP.NET supports several output caching locations for which you can specify using the
OutputCache directive:
Any Page can be cached by the browser, a downstream server, or on the server
Client Page should be cached on the client browser only
Downstream Page should be cached on a downstream server and the client
Server Page will be cached on the server only
None Disable caching
The HttpCachePolicy also allows you to determine the location of the cached content pro-
grammatically. This is done through the HttpCachePolicy.SetCacheability method (or the
HttpResponse.CacheControl property), which takes a parameter of the HttpCacheability enu-
meration. The enumeration is a bit easier to read than the attributes used in the OutputCache
directive. They include:
the output caching variables (such as Duration). When you mention the profi le name in the
OutputCache directive, ASP.NET will simply read the values out of the confi guration fi le and
apply them to the OutputCache directive.
The following exercise illustrates setting up a cache profi le instead of hard-coding the values
into the page.
Set up a cache profi le
1. Add a cache profi le to the site’s web.confi g fi le. If web.confi g isn’t already there, go
ahead and add one to the project. Then add a cache profi le to web.confi g nested be-
tween the system.web opening and closing tags. Name the cache profi le profi le.
<configuration>
<system.web>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="profile"
duration="60"
varyByParam="TextBoxName" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
</system.web>
</configuration>
Chapter 16 Caching Output 363
2. Change the OutputCache directive in the Default.aspx page to use the new profi le:
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default"
trace="false"%>
<%@ OutputCache CacheProfile="profile" %>
3. Surf to the page. It should work exactly as it did before when the caching values were
hard-coded. That is, run the page, type a name, and note the date and time stamp.
3. Create a new page in the project. Name it UseSiteMenuControl.aspx.
Chapter 16 Caching Output 365
4. Drag the SiteMenu User control onto the UseSiteMenuControl page. When ASP.NET
loads and runs your Web page, ASP.NET will cache the User control because the User
control mentions the OutputDirective.
5. Make sure tracing is turned on in the UseSiteMenuControl.aspx fi le. (That is, set the
Trace=”true” attribute in the Page directive.) Surf to the page. The fi rst time you surf to the
page, you’ll see the following information in the control tree section of the Trace output:
Notice the entire control tree was rendered. Push the refresh key (F5 in Internet
Explorer) while looking at UseSiteMenuControl.aspx. Examine the control tree portion
of the Trace output again. Notice that ASP.NET uses the cached control instead of re-
rendering the entire SiteMenu control.
366 Part III Caching and State Management
When Output Caching Makes Sense
As with other caching techniques, one of the most effective strategies is to turn on output
caching for those pages that are accessed frequently but yet are expensive to generate. Also,
be sure to cache only those pages that don’t change frequently (otherwise, you may be bet-
ter off simply not using output caching).
For example, pages full of controls that render a great deal of HTML are probably expensive.
Imagine a page including a DataGrid displaying an employee directory. This is a perfect can-
didate for caching for several reasons. First, a database access (or even an in-memory cache
hit) is required. Second, a DataGrid is pretty expensive to render—especially if it needs to
fi gure out the schema of the employee directory table on the fl y. Finally, an employee direc-
tory probably doesn’t change very often. By caching it once, you can avoid spending a great
deal of unnecessary cycles.
A related issue here is to be careful when typing asterisks into the output caching parameters
such as VaryByParam. Using VaryByParam=* tells ASP.NET to generate a new page for every
single request in which any query string parameter has changed. That’s almost the same as
navigation controls or some other control that doesn’t change very often. By applying the
OutputCache directive to the User control, ASP.NET caches that part of the page on which it
was placed.
368 Part III Caching and State Management
Chapter 16 Quick Reference
To Do This
Cache a page’s output
Add the OutputCache directive to the page.
Store multiple versions of a page based on
varying query string parameters
Use the VaryByParam attribute of the OutputCache directive.
Store multiple versions of a page based on
varying headers
Use the VaryByHeader attribute of the OutputCache directive.
Store multiple versions of a page based on
varying browsers
Use the VaryByCustom attribute of the OutputCache directive,
selecting browser as the value.
Specify the location of the cached content
Specify the Location attribute in the OutputCache directive.
Access caching attributes programmatically
Use the Cache property of the Response object, which is an
instance of the HttpCachePolicy class.
Offl oad output caching confi guration to the
web.confi g fi le
Add outputCacheProfi le elements to your web.confi g fi le. Use
them as necessary.
Cache a User control
Apply the OutputCache directive to the control’s ASCX fi le.
To
client really gets to see is a snapshot of the application. This chapter shows you how to watch
your application as it runs and how to trace the state of any particular request. We’ll also
cover managing error pages and trapping application exceptions within ASP.NET.
Page Tracing
The fi rst place to start with debugging is to examine ASP.NET page tracing. The Page class
has a property named Trace. When Trace is turned on, it tells the ASP.NET runtime to insert a
rendering of the entire context of the request and response at the end of the HTML sent to
the client.
We’ve already seen page tracing to some extent. When we looked at the ASP.NET server-
side control architecture, the page trace was invaluable in understanding the structure of the
page. Remember, a rendered page is composed of a number of server-side controls collected
as a hierarchical tree. A Page nests several controls, and the controls themselves may nest
other controls (they may be nested several levels deep, as a matter of fact). The page trace
includes a section displaying the composition of the page in terms of server-side controls.
372 Part IV Diagnostics and Plumbing
Turning on Tracing
Turning on tracing is easy. Simply set the Trace property of the page to true. You may turn on
tracing either by modifying the ASPX code directly or by setting the Trace property using the
designer. Here’s the Trace property being turned on directly within the ASPX code as part of
the page directive.
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="TraceMe.aspx.cs"
Inherits="TraceMe" Trace="true" %>
As soon as you turn tracing on and surf to the page, you’ll see tracing information appear at
the end of the HTML stream. Listing 17-1 shows some code from the DebugORama example
that came with the CD accompanying this book. The TraceMe.aspx page builds a table of
strings as they’re entered on the site. The list of strings is kept in session state and refreshes
the table every time a new string is submitted.
LISTING 17-1 Code That Builds a Table on Loading
public partial class TraceMe : System.Web.UI.Page
{
Figure 17-1 shows how the page appears with tracing turned on.
FIGURE 17-1 Tracing turned on for the application in Listing 17-1
A bit farther down the tracing output, you’ll see the control tree (as we saw in several earlier
chapters). The control tree for this page is shown in Figure 17-2. FIGURE 17-2 Tracing turned on for the application in Listing 17-1. Notice the control tree.
374 Part IV Diagnostics and Plumbing
Finally, scroll down a bit more and you start seeing some of the context information associated
with the request. Figures 17-3 and 17-4 show some of this context information. This application
uses session state to save the array of strings. Notice that the session state tracing shows the
contents of the session state dictionary. You also get to see other context information. For ex-
ample, the tracing section shows the session ID and the URL used to surf to this page. FIGURE 17-3 Tracing turned on for the application in Listing 17-1. Note the detailed information about the
context of the request.
Of course, much of this information becomes more useful in cases in which there’s a problem
with your Web site. For example, the table might stop building itself because you somehow
removed the session state item holding the list of strings. You could detect that by examining
the page trace. If users begin to complain about layout issues with your site, you may look at
the user agent coming down with the request and learn that the client is using a browser not
accommodated by your application.
Chapter 17 Diagnostics and Debugging 375
FIGURE 17-4 Tracing turned on for the application in Listing 17-1. Note the detailed information about the
context of the request.
Trace Statements
In addition to all the request context information included with the HTML stream, the page
ArrayList alTableEntries = null;
protected void Page_Load(object sender, EventArgs e)
{
alTableEntries = (ArrayList)this.Session["TableEntries"];
if (alTableEntries == null)
{
Trace.Warn("Page_Load", "alTableEntries is null");
alTableEntries = new ArrayList();
}
AssembleTable();
}
protected void AssembleTable()
{
this.Table1.Rows.Clear();
foreach (String s in alTableEntries)
{
Trace.Write("AssembleTable", "String found: " + s);
TableRow row = new TableRow();
TableCell cell = new TableCell();
cell.Text = s;
row.Cells.Add(cell);
this.Table1.Rows.Add(row);
}
}
protected void Button1_Click(object sender, EventArgs e)
{
Trace.Write("Button1_Click", "Adding string: " + this.TextBox1.Text);
alTableEntries.Add(this.TextBox1.Text);
this.Session["TableEntries"] = alTableEntries;
AssembleTable();
false
Enable or disable application-level tracing
localOnly
true
false
Specify whether to show trace output only on local
host or everywhere
mostRecent
true
false
Specify whether to recycle traces once requestLimit
is met or to keep the fi rst N (up to the requestLimit
threshold)
pageOutput
true
false
Specify whether to display trace output on individ-
ual pages in addition to caching application-level
traces
requestLimit
Decimal number Specify how many traces to store in memory be-
fore removing earlier traces (default is 10)
writeToDiagnosticsTrace
true
false
Specify whether the trace data are also piped to
System.Diagnostics.Trace
The following exercise demonstrates how application-level tracing works and how to navi-
gate around the results.
Ke
Web page.
Enabling Tracing Programmatically
Although much of the time you’ll fi nd yourself enabling tracing via the designer, there are
times when it’s useful to manage tracing during run time (programmatically). For example,
you might have regular clients receive normal content; however, when someone with specifi c
credentials appears, you might want to enable tracing for that individual. You might also de-
cide to modify tracing when a certain parameter comes through the request.
The DebugORama site includes a page named EnableTracing.aspx that illustrates how to con-
trol the tracing programmatically. If the user types the correct password, the tracing is turned
on. The page also shows how to enable and disable tracing programmatically.
public partial class EnableTracing : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
382 Part IV Diagnostics and Plumbing
if (this.TextBoxSecretCode.Text == "password")
{
this.Trace.IsEnabled = true;
}
}
protected void Button2_Click(object sender, EventArgs e)
{
this.Trace.IsEnabled = false;
}
}
The TraceFinished Event
The tracing context includes an interesting event named TraceFinished that gives you a last
another setting you can set in web.confi g, but notice this lies outside the normal System.web
section of web.confi g.
<system.codedom>
<compilers>
<compiler compilerOptions="/d:TRACE" />
</compilers>
</system.codedom>
Debugging with Visual Studio
The tracing support built into ASP.NET works really well and is a great way to debug your ap-
plication—especially once it’s deployed. However, when you’re in development mode, having
to plant tracing messages into your page and then run it to see what happened is old school
and sometimes not the most effi cient way of debugging. Visual Studio provides excellent
debugging support through the environment, and you may use it to watch your code ex-
ecute and to step through the code one line at a time. In fact, you have access to all of Visual
Studio’s debugging facilities, even though you’re developing Web applications.
Remember, ASP.NET and Visual Studio work in concert to make it feel like you’re doing
desktop application development, even though it’s a Web application. That goes for the de-
bugger as well. The following exercise will familiarize you with the Visual Studio debugging
environment.
Debug an application
1. Open the DebugORama Web site. To support debugging, web.confi g needs to include
the right settings. You may type the debugger setting in by hand if you wish; however,
Visual Studio will insert it for you once you start debugging.
<system.web>
<compilation debug="true"/>
</system.web>
2. Open the TraceMe.aspx page and insert breakpoints in Page_Load, AssembleTable, and
Button1_Click. You may insert breakpoints by highlighting a line in the editor window
and pressing the F9 key. You may also select Debug, Toggle Breakpoint from the
main menu or simply click on the light gray ribbon to the left of the text in the code