Output caching is the caching of pages or
portions of pages that are output to the client. This does not happen
automatically -- the developer must enable output caching using
either the OutputCache
page directive or the
HttpCachePolicy class. Both methods will be described.
Output caching can be applied to an entire page or a portion of the page. In order to cache only a portion of a page, the caching is applied to a user control contained within the page. This too will be described later in this section.
The OutputCache
page directive, like all page
directives, goes at the top of the page file. (For a complete
description of page directives, see Chapter 6.) A
typical example of an OutputCache
page directive
looks something like the following:
<%@ OutputCache Duration="60" VaryByParam="*" %>
The full syntax is:
<%@ OutputCache Duration="number of seconds" VaryByParam="parameter list" Location="location" VaryByControl="control list" VaryByCustom="custom output" VaryByHeader= "header list" %>
Only the first two parameters, Duration
and
VaryByParam
, are required
The VaryBy
... parameters allow different versions
of the cached page to be stored, with each version satisfying the
combination of conditions being varied.
The various parameters are described in the following sections.
The Duration
parameter specifies the
number of seconds that the page or user control is cached. Items
placed in the output cache are only valid for this specified time
period. When the time limit is reached, then the cache is said to be
expired. The next request for the cached page or
user control after the cache is expired causes the page or user
control to be regenerated, and the cache is refilled with the fresh
copy.
An example will clarify this. Example 18-1 and Example 18-2 show the VB.NET and C# versions, respectively, of a very simple web page with output caching implemented. Each time the page is loaded, it will display the time in a Label control. The HTML is omitted from Example 18-2, since it is identical to that in Example 18-1. Running the page in a browser gives the result shown in Figure 18-1.
Example 18-1. Simple output caching in VB.NET, vbOutputCache-01.aspx
<%@ Page Language="VB" %>
<%@ OutputCache Duration="10" VaryByParam="*" %>
<script runat="server">
sub Page_Load(ByVal Sender as Object, _
ByVal e as EventArgs)
lblMsg.Text = "This page was loaded at " & _
DateTime.Now.ToString("T")
end sub
</script>
<html>
<body>
<form runat="server">
<h1>Output Caching</h1>
<asp:Label
id="lblMsg"
runat="server"/>
</form>
</body>
</html>
In Example 18-1 and Example 18-2, the only thing necessary to implement output
caching is the second line in the listing, the
OutputCache
page directive. It specifies a
Duration
of 10 seconds. (The other parameter,
VaryByParam
, will be explained in the next
section.) This means that if the same page is requested from the
server within 10 seconds of the original request, the subsequent
request will be served out of the cache, rather than being
regenerated by ASP.NET.
This is easy to verify. Run the page and note the time. Then quickly refresh the page in the browser. If you refresh within 10 seconds of originally running the page, the displayed time will not have changed. You can refresh the page as many times as you wish, but the displayed time will not change until 10 seconds have passed.
The
VaryByParam
parameter allows you to cache different versions of the page
depending on which parameters are submitted to the server when the
page is requested. These parameters are contained in a
semicolon-separated list of strings.
In the case of a GET request, the strings in the parameter list represent query string values contained in the URL. In the case of a POST request, the strings represent variables sent as part of the form.
There are two special values for the VaryByParam
parameter:
Value |
Description |
---|---|
|
Don’t vary by parameter -- i.e., save only a single version of the page in the cache and return that version no matter what query string values or form variables are passed in as part of the request. |
|
Save a separate version of the page in cache for each unique
combination of query string values or form variables. The order of
the query string values or form variables have no effect on the
caching. However, the parameter values are case sensitive:
|
To see the effects of the VaryByParam
parameter,
modify the previous example. Add two labels for displaying parameters
passed in as a query string as part of the URL in a GET request.
Also, change the Duration
parameter to 60 seconds
to give you more time to explore the effects. The resulting
.aspx
page is shown in Example 18-3 (for VB.NET) and 18-4 (for C#). (The HTML is
omitted from Example 18-4 since it is identical to
that in 18-3.)
Example 18-3. Output caching using the VaryByParam parameter in VB.NET, vbOutputCache-02.aspx
<%@ Page Language="VB" %><%@ OutputCache Duration="60" VaryByParam="*" %>
<script runat="server"> sub Page_Load(ByVal Sender as Object, _ ByVal e as EventArgs) lblMsg.Text = "This page was loaded at " & _ DateTime.Now.ToString("T")lblUserName.Text = Request.Params("username")
lblState.Text = Request.Params("state")
end sub </script> <html> <body> <form runat="server"> <h1>Output Caching</h1> <asp:Label id="lblMsg" runat="server"/> <br/> <br/>UserName:
<asp:Label
id="lblUserName"
runat="server"/>
<br/>
State:
<asp:Label
id="lblState"
runat="server"/>
</form> </body> </html>
Example 18-4. Output caching using the VaryByParam in parameter in C#, csOutputCache-02.aspx
<%@ Page Language="C#" %> <%@ OutputCache Duration="60" VaryByParam="*" %> <script runat="server"> void Page_Load(Object Source, EventArgs E) { lblMsg.Text = "This page was loaded at " + DateTime.Now.ToString("T");lblUserName.Text = Request.Params["username"];
lblState.Text = Request.Params["state"];
} </script>
To test Example 18-3 and Example 18-4, enter the following URL in a browser:
http://localhost/progaspnet/vbOutputCache-02.aspx?username=Dan&state=MA
This will give the result shown in Figure 18-2.
Now enter the same URL but with different parameters, say
username=Jesse
and state=NY
, as
in:
http://localhost/progaspnet/vbOutputCache-02.aspx?username=Jesse&state=NY
This will give a different time in the resulting page. Now go back
and enter the original URL with username=Dan
and
state=MA
. You will see the original time shown in
Figure 18-2, assuming 60 seconds have not passed
since you first entered the URL.
Suppose the above example was part of an application where the
username
was needed for login purposes and the
state
was used to query a database to return
information about publicly traded firms in that state. In that case,
it would make no sense to cache based on the
username
, but it would make a lot of sense to
cache based on the state
parameter.
To accomplish this, set VaryByParam
equal to the
parameter(s) you wish to cache by. So, for example, to cache only by
state
, use the following OutputCache directive:
<%@ OutputCache Duration="60" VaryByParam="state" %>
If you need to cache by the unique combination of two parameters, say
state
and city
, use a directive
similar to:
<%@ OutputCache Duration="60" VaryByParam="state;city" %>
The Location
parameter specifies the
hard drive where the cached data is stored. The permissible values
for this parameter are contained in the
OutputCacheLocation
enumeration (see Table 18-1).
Table 18-1. Location parameter values
Parameter value |
Description |
---|---|
The cache is located on the same machine as the client browser. Useful if the page requires authentication. | |
The cache is located on a server downstream from the web server. This might be a proxy server. | |
The cache is located on the web server processing the request. | |
Output caching is disabled. | |
The output cache can be located either on the client, on a downstream server, or on the web server. This is the default value. |
The Location
parameter is not supported when
output caching user controls.
The VaryByControl
parameter is
used when caching user controls, which will be described in Section 18.2.2 later in this chapter. This
parameter is not supported in OutputCache
directives in web pages (.aspx
files).
The values for this parameter consist of a semicolon-separated list of strings. Each string represents a fully qualified property name on a user control.
The VaryByCustom
parameter
allows the cache to be varied by browser if the value of the
parameter is set to browser
. In this case, the
cache is varied by browser name and major version. In other words,
there will be separate cached versions of the page for IE 4, IE 5,
Netscape 6, or any other browser type or version used to access the
page.
All of the examples shown so far have cached the entire page. Sometimes all you want to cache is part of the page. In order to do this, wrap that portion of the page you wish to cache in a user control and cache just the user control. This is known as fragment caching. (For a complete discussion of user controls, see Chapter 14.)
For example, suppose you develop a stock portfolio analysis page, where the top portion of the page displays the contents of the user’s stock portfolio, and the bottom portion contains a data grid showing historical data about one specific stock. There would be little benefit in caching the top portion of the page, since it will be different for every user. However, it is likely that in a heavily used web site, many people will be requesting historical information about the same stock, so there would be benefit to caching the bottom portion of the page. This is especially true since generating the historical data requires a relatively expensive database query. In this case, you can wrap the data grid in a user control and cache just that.
To demonstrate fragment caching, create the very simple user control shown in Example 18-5 using VB.NET and in Example 18-6 using C#.
Example 18-5. Simple user control in VB.NET, vbUserControl.ascx
<%@ Control Language="VB" %>
<%@ OutputCache Duration="10" VaryByParam="*" %>
<script runat="server">
sub Page_Load(ByVal Sender as Object, _
ByVal e as EventArgs)
lblMsg.Text = "This User Control was loaded at " & _
DateTime.Now.ToString("T")
end sub
</script>
<hr/>
<h1>User Control</h1>
<asp:Label
id="lblMsg"
runat="server"/>
<hr/>
Example 18-6. Simple user control in C#, csUserControl.ascx
<%@ Control Language="C#" %>
<%@ OutputCache Duration="10" VaryByParam="*" %>
<script runat="server">
void Page_Load(Object Source, EventArgs E)
{
lblMsg.Text = "This User Control was loaded at " +
DateTime.Now.ToString("T");
}
</script>
<hr/>
<h1>User Control</h1>
<asp:Label
id="lblMsg"
runat="server"/>
<hr/>
This user control does nothing more than display the time it was
loaded. The visible portion of the control is surrounded by
horizontal rules (<hr/>
) to distinguish it
when it is used in a web page. Notice that the
OutputCache
directive specifies a
Duration
of 10 seconds.
Now create a web page to use this user control, as shown in Examples Example 18-7 and Example 18-8, in VB.NET and C#, respectively.
Example 18-7. Fragment caching demo in VB.NET, vbOutputCache-UserControl.aspx
<%@ Page Language="vb" %><%@ Register TagPrefix="SampleUserControl" TagName="LoadTime"
Src="vbUserControl.ascx" %>
<script runat="server"> sub Page_Load(ByVal Sender as Object, _ ByVal e as EventArgs) lblMsg.Text = "This page was loaded at " & _ DateTime.Now.ToString("T") end sub </script> <html> <body> <form runat="server"> <h1>Fragment Caching</h1> <asp:Label id="lblMsg" runat="server"/> <br/><SampleUserControl:LoadTime
runat="server"/>
</form> </body> </html>
Example 18-8. Fragment caching demo in C#, csOutputCache-UserControl.aspx
<%@ Page Language="C#" %><%@ Register TagPrefix="SampleUserControl" TagName="LoadTime"
Src="csUserControl.ascx" %>
<script runat="server"> void Page_Load(Object Source, EventArgs E) { lblMsg.Text = "This page was loaded at " + DateTime.Now.ToString("T"); } </script> <html> <body> <form runat="server"> <h1>Fragment Caching</h1> <asp:Label id="lblMsg" runat="server"/> <br/><SampleUserControl:LoadTime
runat="server"/>
</form> </body> </html>
Notice that the web page that uses the user control does not have any
caching implemented; there is no OutputCache
directive.
When you run the web page from Example 18-7 or Example 18-8 in a browser, you will initially see something like Figure 18-3.
The time displayed for both the user control and the containing page are the same. However, if you refresh the view, you will notice that the time the page was loaded will be the current time, while the time the user control was loaded is static until the 10-second cache duration has expired.
One caveat to keep in mind when caching user controls is that it is not possible to programmatically manipulate the user control being cached. This is because a user control in cache is only generated dynamically the first time it is requested. After that, the object is not available for the code to interact with. If you need to manipulate the contents of the user control programmatically, the code to do so must be contained within the user control.
To demonstrate this, modify the code in Example 18-5 to add a property called UserName to the sample user control. The new user control is shown (in VB.NET only) in Example 18-9.
Example 18-9. User control with UserName property in VB.NET, vbUserControl-02.ascx
<%@ Control Language="VB" %> <%@ OutputCache Duration="10" VaryByParam="*" %> <script runat="server"> sub Page_Load(ByVal Sender as Object, _ ByVal e as EventArgs) lblMsg.Text = "This User Control was loaded at " & _ DateTime.Now.ToString("T") end subpublic property UserName( ) as string
get
return lblUserName.Text
end get
set
lblUserName.Text = value
end set
end property
</script> <hr/> <h1>User Control</h1><asp:Label
id="lblMsg"
runat="server"/>
<br/> <asp:Label id="lblUserName" Text="Dan" runat="server"/> <hr/>
In Example 18-9, a property named UserName was added
to the code, with both a Get and a Set method. Also, a label was
added to display the UserName. For now, this label is hard-coded to
Dan
.
Now modify the code in Example 18-7 to call this modified user control. The code for this modified page is shown in Example 18-10 (in VB.NET only).
Example 18-10. Fragment caching demo with a property in VB.NET, vbOutputCache-UserControl-02.aspx
<%@ Page Language="vb" %> <%@ Register TagPrefix="SampleUserControl" TagName="LoadTime" Src="vbUserControl-02.ascx" %> <script runat="server"> sub Page_Load(ByVal Sender as Object, _ ByVal e as EventArgs) lblMsg.Text = "This page was loaded at " & _ DateTime.Now.ToString("T")lblUserControlText.Text = MyUserControl.UserName
end sub sub btn_OnClick(ByVal Sender as Object, _ ByVal e as EventArgs)MyUserControl.UserName = "Jesse"
end sub </script> <html> <body> <form runat="server"> <h1>Fragment Caching</h1> <asp:Label id="lblMsg" runat="server"/> <br/> <SampleUserControl:LoadTime ID="MyUserControl" runat="server"/> <br/><asp:Label
id="lblUserControlText"
runat="server"/>
<br/>
<asp:Button
id="btn"
Text="Change Name to Jesse"
OnClick="btn_OnClick"
runat="server"/>
</form> </body> </html>
The code in Example 18-10 adds the highlighted code to populate the lblUserControlText label with the initial value of the lblUserName control contained in the user control. This works fine when the page is first called, giving the result shown in Figure 18-4.
It even works as expected if you click the button to change the name to Jesse. This is because the button causes the form to be posted to the server, so everything is regenerated and the request for the user control is not being satisfied from the cache. However, as soon as you refresh the page and ASP.NET attempts to satisfy the request for the user control from the cache, an error occurs.
The only way around this is to move all the code that accesses the
user control property into the user control itself, as shown in Example 18-11 for the user control. The calling page then
reverts back to the same page shown in Example 18-7 and Example 18-8. (Be certain
to change the name of the Src
parameter in the
Register
directive in Example 18-7 or Example 18-8 to point to
the correct user control.)
Example 18-11. User control setting UserName property in VB.NET, vbUserControl-03.ascx
<%@ Control Language="VB" %> <%@ OutputCache Duration="10" VaryByParam="*" %> <script runat="server"> sub Page_Load(ByVal Sender as Object, _ ByVal e as EventArgs) lblMsg.Text = "This User Control was loaded at " & _ DateTime.Now.ToString("T") end sub public property UserName( ) as string get return lblUserName.Text end get set lblUserName.Text = value end set end propertysub btn_OnClick(ByVal Sender as Object, _
ByVal e as EventArgs)
lblUserName.Text = "Jesse"
end sub
</script> <hr/> <h1>User Control</h1> <asp:Label id="lblMsg" runat="server"/> <br/> <asp:Label id="lblUserName" Text="Dan" runat="server"/> <br/><asp:Button
id="btn"
Text="Change Name to Jesse"
OnClick="btn_OnClick"
runat="server"/>
<hr/>
While this restriction on programmatically modifying user controls that are in the cache might seem significant, as a practical matter it should not be. The entire point of putting user controls in the cache is that they will not change while cached. If that is not the case, then they are probably not a good candidate for caching.
13.59.173.242