In some cases, it's enough to blindly cache the content of certain pages by simply putting the OutputCache
directive in the page. However, sometimes you need a bit more control
over what's happening in the output cache. ASP.NET supports a number of
parameters you can use to manage the way the cache functions. You can
control the output caching behavior by either changing the parameters in the OutputCache directive or tweaking the HttpCachePolicy property available through the Response object.
1. Modifying the OutputCache Directive
It's often very useful to be able to govern output
caching. For example, some pages present exactly the same content to
all the users who access the page. In that case, caching a single
version of the content is just fine. However, there are other
circumstances in which sending the same content to everyone is
inappropriate. The easiest way to control the behavior of output caching is to modify the OutputCache directive.
One obvious case in which
controlling the cache is important is while caching different versions
of content for different browsers making requests. Different browsers
often have different capabilities. If you send content that requires a
feature not supported by all browsers, some browsers making requests
will get a response that they're unable to handle adequately. With the VaryByCustom parameter in the OutputCache directive, you can cache different content based on different browsers.
Controlling the output caching is also important when your page renders content based on the parameters
that are sent in the query string. For example, imagine you have a page
through which a user has identified him- or herself by typing a name in
a text box. The browser inserts that name in a parameter inside the
query list. You can instruct the output cache to cache different
versions based on parameters in the query string. For example, users
who identify themselves as "John Doe" can get a different version of
cached content than can users who identify themselves as "Jane Smith."
The VaryByParam attribute controls this behavior.
Table 1 shows a summary of these parameters.
Table 1. Summary of OutputCache Parameters
Attribute | Option | Description |
---|
CacheProfile | A string | Name of a profile (found in web.config) to control output cache settings. Default is an empty string. |
Duration | number | Number of seconds the page or control is cached (required). |
NoStore | true
false | Specifies that the "no store" cache control header is sent (or not). Not available to user controls. Default value is false. |
Location | Any
Client
Downstream
Server
None | Manages which header and metatags are sent to clients to support caching; here are their meanings:
Any—page can be cached anywhere (default).
Client—cached content remains at browser.
Downstream—cached content is stored both downstream and on the client.
Server—content cached on the server only.
None—disables caching. |
Shared | true
false | Determines whether user control output can be shared with multiple pages. |
SqlDependency | A string representing a database/table name pair | Identifies a set of database and table name pairs on which a page's or control's output cache depends. |
VaryByContentEncoding | encodings | Specifies a list of encoding strings separated by commas used to vary the output cache. |
VaryByCustom | browser
custom string | Tells ASP.NET to vary the output cache by browser name and version or by a custom string; must be handled by an override of GetVaryByCustomString in Global.asax. |
VaryByHeader | *
header names | A
semicolon-delimited list of strings specifying headers that might be
submitted by a client. Not available to user controls. Default value is
an empty string (no headers). |
VaryByParam | None
*
param name | A semicolon-delimited list of strings specifying query string values in a GET request or variables in a POST request (required). |
The following
exercise illustrates creating separate versions of cached content based
on how the user identifies himself or herself.
Varying cached content by query string parameters
Returning to the OutputCache Web application, add a TextBox and a Button to the default.aspx page. Give the TextBox an ID of TextBoxName and the Button an ID of ButtonSubmitName. The TextBox will hold the client's name and will serve as the parameter controlling the number of cached versions of the page.
Double-click the button to add a Click
event handler. In the handler, respond to the user's request by
displaying a greeting using the contents of the text box. Also, modify
the processing time of the page loading by reducing the amount of time
the current thread sleeps (or by commenting out that line):
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Thread.Sleep(0);
Response.Write("This page was generated and cached at: " +
DateTime.Now.ToString());
}
protected void ButtonSubmitName_Click(object sender, EventArgs e)
{
Response.Write("<br><br>");
Response.Write("<h2> Hello there, " +
this.TextBoxName.Text + "</h2>");
}
}
Increase
the time that the content will be cached (this example uses 1 minute)
so that you have time to change the contents of the TextBox to view the effects of caching. Also, include TextBoxName as the parameter by which to vary the content in the OutputCache directive.
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default"
Trace="false"%>
<%@ OutputCache Duration="60" VaryByParam="TextBoxName" %>
Add a Substitution control to the page following the TextBox and the Button. You can just drag one from the Toolbox onto the page. Use the SubstitutionSubstitution controls call back to a method on the code beside that displays arbitrary strings. Write a method in the code-beside class to handle the substitution. control to display the time of the request to compare it with the time displayed by the cached page.
public partial class _Default : System.Web.UI.Page
{
// Existing code ...
protected static string SubstituteDateAndTime(HttpContext c)
{
return "Request occurred at :" + DateTime.Now;
}
}
Set the MethodName attribute of the Substitution control to the SubstituteDateAndTime method in the ASPX file, like this:
<asp:Substitution ID="Substitution1" MethodName="SubstituteDateAndTime"
runat="server" />
Surf
to the page and type in a name. Click the button to submit the form and
note the time stamp of the page. Type a second name in the TextBox
and click the button to submit the form. Note the time stamp. Then,
type the same name you typed the first time. Click the button to submit
the form. If you do all of this within the 60-second window, you should
see the cached versions of the page, which you can discern using the
time stamp displayed as part of each page. The following three graphics
illustrate the caching varying by the value of the TextBoxName parameter. The first graphic shows the original request using a particular name in the TextBox. Notice that the request time shown by the Substitution and the time shown by the Page_Load method are the same.
The second graphic shows a request with a new value for the TextBoxName parameter. Notice that the request time shown by the Substitution and the time shown by the Page_Load method are the same this time as well:
The third graphic shows
making a request to the page using the same name as the original
request. Notice that the request time shown by the Substitution and the time shown by the Page_Load method are different. The request time is earlier than the time shown during the Page_Load method, meaning the page content was cached:
There are other ways to modify the VaryByParam attribute. One way is to use the word none,
which means ASP.NET will cache only one version of the page for each
type of request (for example, GET, POST, and HEAD). Using an asterisk
(*) for VaryByParam tells ASP.NET to cache as many different versions of the page as there are query
string or POST body requests. The previous example caches as many
different versions of the page as there are unique names typed by users
into the name text box.
Using VaryByHeader in the OutputCache directive tells ASP.NET to generate a separate cache entry for each new header string that comes down. (For example, UserAgent and UserLanguage represent HTTP headers that can be sent by the client.)
You will cache a user control shortly. With the VaryByControl attribute, you can cache separate content versions for each page that has a user control with unique properties.
Finally, VaryByCustom
tells ASP.NET to manage separate cache entries dependent on a couple of
factors. The first factor is the browser types and versions.
Alternatively, you can provide a custom GetVaryByCustomString method in Global.asax that tells ASP.NET to create separate cached versions of a page based on a custom defined string.
2. The HttpCachePolicy
The second way to manage the output cache is through the HttpCachePolicy, which is available from the Response class. Table 2 shows a portion of the HttpCachePolicy class.
Table 2. The HttpCachePolicy Class
Member | Description |
AppendCacheExtension | Appends specified text to the Cache-Control HTTP header |
SetCacheability | Sets the Cache-Control HTTP header, which controls how documents are to be cached on the network |
SetETag | Sets the ETag HTTP header to the specified string |
SetExpires | Sets the Expires HTTP header to an absolute date and time |
SetLastModified | Sets the Last-Modified HTTP header to a specific date and time |
SetMaxAge | Sets the Cache-Control: max-age HTTP header to a specific duration |
SetRevalidation | Sets the Cache-Control HTTP header to either the must-revalidate or the proxy-revalidate directives |
SetValidUntilExpires | Determines whether the ASP.NET cache should ignore HTTP Cache-Control headers sent by the client for invalidating the cache |
SetVaryByCustom | Specifies a custom text string for managing varying cached output responses |
VaryByHeaders | Parameter list of all HTTP headers that will be used to vary cache output |
VaryByParam | Parameter list received by a GET (query string) or POST (in the body of the HTTP request) that affects caching |
When you set up an OutputCache directive, you tell ASP.NET to populate this class during the Page class's InitOutputCache method. The Response object makes the HttpCachePolicy available through its Cache property. The name Cache is unfortunate because you might easily confuse it with the application data cache. Perhaps CachePolicy would have been a better name for the property to avoid such confusion. In any case, you can use the HttpCachePolicy class to control the behavior of server-side output caching as well as the headers used for content caching. You can also use the OutputCache directive to control some of the same aspects as the HttpCachePolicy
class. However, some features, such as sliding the expiration date or
changing the "last modified" stamp for a page, are available only
through the HttpCachePolicy class.
For example, Example 1
shows a page fragment ensuring that all origin-server caching for the
current response is stopped. It also sets the last modified date to the
current date and time.
Example 1. Manipulating the output cache policy
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e)
{ Thread.Sleep(0); Response.Write("This page was generated and cached at: " + DateTime.Now.ToString());
Response.Cache.SetNoServerCaching(); Response.Cache.SetLastModified(DateTime.Now); } }
|
3. Caching Locations
In addition to varying the
number of cached versions of a page, you can tell ASP.NET where to
cache the content. This is controlled through either the LocationHttpCachePolicy class's SetCacheability method. attribute in the OutputCache directive or by using the
ASP.NET supports several output caching locations that 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.
With the HttpCachePolicy, you can also determine the location of the cached content programmatically. This is done through the HttpCachePolicy.SetCacheabilityHttpResponse.CacheControl property), which takes a parameter of the HttpCacheability enumeration. The enumeration is a bit easier to read than the attributes used in the OutputCache directive are. They include the following: method (or the
NoCache Disable caching.
Private Only cache on the client.
Public Cache on the client and the shared proxy.
Server Cache on the server.
ServerAndNoCache Specify that the content is cached at the server but all others are explicitly denied the ability to cache the response.
ServerAndPrivate
Specify that the response is cached at the server and at the client but
nowhere else; proxy servers are not allowed to cache the response.
4. Output Cache Dependencies
The contents of the
application data cache in ASP.NET can be flushed because of various
dependencies. The same is true of ASP.NET output caching. The response object has a number of methods for
setting up dependencies based on cached content. For example, you might
want to set up a page that renders data from a text file. You can set
up a CacheDependency on that text file so that when the text file is changed, the cached output is invalidated and reloaded.
5. Caching Profiles
One of the problems associated with using the OutputCache
directive directly is that the values become hard-coded. Changing the
caching behavior means going in and changing the source code of the
page. A feature added to ASP.NET 2.0 and later versions is the ability
to add caching profiles. That way, setting the caching behavior variables is offloaded to the configuration file, and output caching becomes an administration issue and not a programming issue (as it should be).
The web.config file can include an outputCacheSettings section that contains a list of outputCacheProfiles. The outputCacheProfiles are simply key/value pairs whose keys are the output caching variables (such as Duration). When you mention the profile name in the OutputCache directive, ASP.NET simply reads the values out of the configuration file and applies them to the OutputCache directive.
The following exercise illustrates how to set up a cache profile instead of hard coding the values into the page.
Setting up a cache profile
Add
a cache profile to the site's web.config file. If web.config isn't
already there, go ahead and add one to the project. Then, add a cache
profile to web.config nested between the system.web opening and closing
tags. Name the cache profile profile.
<configuration>
<system.web>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="profile"
duration="60"
varyByParam="TextBoxName" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
</system.web>
</configuration>
Change the OutputCache directive in the Default.aspx page to use the new profile:
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default"
trace="false"%>
<%@ OutputCache CacheProfile="profile" %>
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. Type a new name and note the date and time
stamp. Type the original name, submit it, and you should see the
original cached page appear (as long as you complete the post within
the specified time window).