Section 17.3. Output Caching

17.3. Output Caching

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 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. To cache only a portion of a page, the caching is applied to a user control contained within the page. This will be described later in this section.

17.3.1. The OutputCache Directive

The OutputCache 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 directive looks something like the following:

 <%@ OutputCache Duration="60" VaryByParam="*" %> 

The full syntax is:

 <%@ OutputCache        Duration="number of seconds"        VaryByParam="parameter list"        CacheProfile=""        DiskCacheable=""        Locaton="Any  Client  Downstream  Server  None"        NoStore=""        SqlDependency="   database   :   table   "        VaryByControl="control list"        VaryByCustom="custom output"        VaryByHeader="header list" %> 

Only the first two parameters, Duration and VaryByParam , are required, though the VaryByParam attribute will not be required for user controls if there is a VaryByControl attribute.

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.

17.3.1.1. Duration

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. Create a new web site called OutputCaching , which contains only a Label control on the content page and a Page_Load that populates that Label control with the current time similar to the way you displayed the current time in the previous example. Add the OutputCache directive highlighted near the top of the content file in Example 17-5. The complete content file is listed in Example 17-5 and the complete code-behind file is listed in Example 17-6.

Example 17-5. Default.aspx for OutputCaching
 <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs"    Inherits="_Default" %>  <%@ OutputCache Duration="10" VaryByParam="*" %>  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server">     <<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <%@ OutputCache Duration="10" VaryByParam="*" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server">     <title>Output Caching</title> </head> <body>     <form id="form1" runat="server">     <div>       <h1>Output Caching</h1>  <asp:Label ID="lblTime" runat="server" />  </div>     </form> </body> </html> 

Example 17-6. Default.aspx.cs for OutputCaching
 using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.Page {     protected void Page_Load(object sender, EventArgs e)     {  lblTime.Text = "This page was loaded at " +            DateTime.Now.ToLongTimeString(  );  } } 

This OutputCache directive is all that is necessary to implement output caching. 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 be regenerated by ASP.NET.

This is easy to verify. Run the page and note the time. 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.

17.3.1.2. VaryByParam

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

none

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: state=ma differs from state=MA .


To see the effects of the VaryByParam parameter, modify the previous example, OutputCaching . 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 complete content file, with the changed code highlighted, is listed in Example 17-7.

Example 17-7. Default.aspx for OutputCaching demonstrating VaryByParam
 <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs"    Inherits="_Default" %>  <%@ OutputCache Duration="60" VaryByParam="*" %>  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/ DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server">     <title>Output Caching</title> </head> <body>     <form id="form1" runat="server">     <div>       <h1>Output Caching</h1>       <asp:Label ID="lblTime" runat="server" />  <br/>       <br/>       UserName:&nbsp;&nbsp;&nbsp;       <asp:Label ID="lblUserName" runat="server" />       <br/>       State:&nbsp;&nbsp;&nbsp;       <asp:Label ID="lblState" runat="server" />  </div>     </form> </body> </html> 

In the code-behind file, add two lines to the Page_Load method to populate the additional Labels . The complete code-behind file is listed in Example 17-8 with the additional lines highlighted.

Example 17-8. Default.aspx.cs for OutputCaching demonstrating VaryByParam
 using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.Page {     protected void Page_Load(object sender, EventArgs e)     {        lblTime.Text = "This page was loaded at " +          DateTime.Now.ToLongTimeString(  );  lblUserName.Text = Request.Params["username"];        lblState.Text = Request.Params["state"];  } } 

To test the version of OutputCaching listed in Examples 17-7 and 17-8, you will have to run the page from a browser outside of VS2005 so you can include query strings as part of the URL. Go to Computer Management and create a virtual directory pointing to the folder where this web site is located. Call the virtual directory OutputCaching , open a browser, and enter the following URL:

 http://localhost/OutputCaching/default.aspx?username=Dan&state=MA 

This will give the result shown in Figure 17-2.

Figure 17-2. OutputCaching demonstrating VaryByParam

Now enter the same URL but with different parameters, say username=Jesse and state=NY , as in:

 http://localhost/OutputCaching/default.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 17-2, assuming 60 seconds have not passed since you first entered the URL.

Suppose the previous 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 this:

 <%@ OutputCache Duration="60" VaryByParam="state;city" %> 

17.3.1.3. CacheProfile

If you find yourself using the same OutputCache directives on many pages, you can use CacheProfiles to reuse the same attributes. To do this, add an <outputCacheProfiles> section to a configuration file. The <add> element specifies the name of the profile, as well as all the directive attributes, as in the following code snippet:

 <outputCacheProfiles>        <add name="StateCityCacheProfile"             enabled="true"             duration="60"             varyByParam=" state;city " />     </outputCacheProfiles> 

To make this CacheProfile available to all the pages on the server, add it either to machine.config or the globally scoped web.config file. To make it available only to a specific web site, add it to the web.config file in the application root. (Configuration files are covered in detail in Chapter 18.)

To use this profile, use the CacheProfile attribute of the OutputCache directive as in the following:

 <%@ OutputCache CacheProfile="StateCityCacheProfile" %> 

17.3.1.4. DiskCacheable

ASP.NET will remove items from cache if the amount of memory available falls below a certain threshold, in a practice known as scavenging . This is described later in this chapter. By default, ASP.NET (in a feature new to Version 2.0) also saves the output cache to disk. That way, the data can be retrieved from disk rather than regenerated from scratch even if memory is short. In addition, this enables cached data to survive an application restart.

To disable disk caching, set the DiskCacheable attribute to false , as in the following:

 <%@ OutputCache Duration="60" VaryByParam="*" DiskCacheable="false" %> 

17.3.1.5. Location

The Location parameter specifies the machine where the cached data is stored. The permissible values for this parameter are contained in the OutputCacheLocation enumeration (see Table 17-3).

Table 17-3. Location parameter values

Parameter value

Description

Client

The cache is located on the same machine as the client browser. Useful if the page requires authentication.

Downstream

The cache is located on a server downstream from the web server. This might be a proxy server.

Server

The cache is located on the web server processing the request.

None

Output caching is disabled.

Any

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.

17.3.1.6. SqlDependency

The SqlDependency attribute of the OutputCache directive allows you to expire the output cache when the underlying data changes. To implement this requires setting up the database and editing web.config , as described in the previous section, "SQL cache dependency."

To see how this works, copy the example used in that section, WebNorthwindCache , to a new web site, called WebNorthwindSqlDependency .

Remove the EnableCaching and CacheDuration attributes from the SqlDataSource declaration. Add the following OutputCache directive to the top of the content file:

 <%@ OutputCache SqlDependency="Northwind:Customers"          Duration="600"  VaryByParam="none" %> 

The value supplied to the SqlDependency attribute is a concatenation of the database name that was previously set up for SQL cache dependency, specified in the web.config file listed in Example 17-4, and the table that was also previously set up for SQL cache dependency, in this case the Customers table. The database name and table name are separated by a semicolon.

In this example, the OutputCache duration is set for 10 minutes and VaryByParam is disabled ( none ).

When you run the page, you can refresh the page all you want and the displayed timestamp will not be updated for 10 minutes. However, go into the database, change a value in the Customers table, and refresh the page, and the timestamp will be updated immediately.

You can see that the page will expire whenever the specified duration is exceeded or the data changes, whichever occurs first.

17.3.1.7. VaryByControl

The VaryByControl parameter is used when caching user controls, which will be described in the next section, "Fragment Caching: Caching Part of a Page." 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.

17.3.1.8. VaryByCustom

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, IE 6, Netscape 6, or any other browser type or version used to access the page.

17.3.1.9. VaryByHeader

The VaryByHeader parameter allows the cache to by varied by HTTP header. The value of the parameter consists of a semicolon-separated list of HTTP headers. This parameter is not supported in OutputCache directives in user controls.

17.3.2. Fragment Caching: Caching Part of a Page

All the examples shown so far have cached the entire page. Sometimes all you want to cache is part of the page. To do this, wrap that portion of the page you want to cache in a user control and cache 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 differ 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 a 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 a new web site called FragmentCaching . Before adding any content to default.aspx , add a new Web User Control to the project and call it SimpleUserControl.ascx . The content file for SimpleUserControl is listed in Example 17-9 and the associated code-behind file is listed in Example 17-10.

Example 17-9. SimpleUserControl.ascx for FragmentCaching
 <%@ Control Language="C#" AutoEventWireup="true"       CodeFile="SimpleUserControl.ascx.cs"       Inherits="SimpleUserControl_ascx" %>  <%@ OutputCache Duration="10" VaryByParam="*" %>  <hr /> <h3>User Control</h3> <asp:Label ID="lblTime" runat="server" /> <hr /> 

Example 17-10. SimpleUserControl.ascx.cs for FragmentCaching
 using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class SimpleUserControl_ascx : System.Web.UI.UserControl {     protected void Page_Load(object sender, EventArgs e)     {        lblTime.Text = "This user control was loaded at " +           DateTime.Now.ToLongTimeString(  );     } } 

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. The OutputCache directive, highlighted in the content file in Example 17-9, specifies a Duration of 10 seconds.

Once the user control is ready, you can add some content to Default.aspx , including placing the user control. The complete listing of Default.aspx is shown in Example 17-11. The lines of code related to implementing the user control are highlighted.

Example 17-11. Default.aspx for FragmentCaching
 <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs"    Inherits="_Default" %>  <%@ Register TagPrefix="MyUserControl" TagName="LoadTime"    Src="~/SimpleUserControl.ascx"%>  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server">     <title>Fragment Caching</title> </head> <body>     <form id="form1" runat="server">     <div>       <h1>Fragment Caching</h1>        <asp:Label ID="lblTime" runat="server" />        <br />  <MyUserControl:LoadTime runat="server" />  </div>     </form> </body> </html> 

The associated code-behind file is listed in Example 17-12.

Example 17-12. Default.aspx.cs for FragmentCaching
 using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.Page {     protected void Page_Load(object sender, EventArgs e)     {        lblTime.Text = "This page was loaded at " +           DateTime.Now.ToLongTimeString(  );     } } 

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 FragmentCaching example, you will initially see something like Figure 17-3.

The time displayed for both the user control and the containing page are identical. However, if you refresh the view, you will notice that the time the page was loaded will be the current time and the time the user control was loaded is static until the 10-second cache duration has expired.

Figure 17-3. FragmentCaching initial screen

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 unavailable 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, copy the FragmentCaching web site to a new web site called FragmentCachingWithProperty . Add a Label control to the user control to display a user's name. For now, this label is hard-coded to Dan . Also add a public property, called Username , with a Get and a Set method to the code-behind file of the user control. The modified content and code-behind files are listed in Examples 17-13 and 17-14, respectively, with the code additions highlighted.

Example 17-13. SimpleUserControl.ascx for FragmentCachingWithProperty
 <%@ Control Language="C#" AutoEventWireup="true"    CodeFile="SimpleUserControl.ascx.cs"    Inherits="SimpleUserControl_ascx" %> <%@ OutputCache Duration="10" VaryByParam="*" %> <hr /> <h3>User Control</h3> <asp:Label ID="lblTime" runat="server" />  <br /> <asp:Label ID="lblUserName" runat="server" Text="Dan" />  <hr /> 

Example 17-14. SimpleUserControl.ascx.cs for FragmentCachingWithProperty
 using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class SimpleUserControl_ascx : System.Web.UI.UserControl {  public string UserName    {       get       {          return lblUserName.Text;       }       set       {          lblUserName.Text = value;       }    }  protected void Page_Load(object sender, EventArgs e)     {        lblTime.Text = "This user control was loaded at " +           DateTime.Now.ToLongTimeString(  );     } } 

Now modify the default page, as shown in the highlighted code in Example 17-15 and its associated code-behind file in Example 17-16. In this content file, you will add an ID attribute to the user control, so you can refer to it elsewhere in the code, plus a Label to display the value of the user control property, UserName , and a button to change the value of the property. The code-behind file adds a line in the Page_Load method to populate that label, as well as a Click event handler for the button.

This example demonstrates the error that occurs when attempting to manipulate a cached user control programmatically.


Example 17-15. Default.aspx for FragmentCachingWithProperty
 <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs"     Inherits="_Default" %> <%@ Register TagPrefix="MyUserControl"  TagName="LoadTime"     Src="~/SimpleUserControl.ascx"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server">     <title>Fragment Caching</title> </head> <body>     <form id="form1" runat="server">     <div>       <h1>Fragment Caching</h1>        <asp:Label ID="lblTime" runat="server" />        <br />        <MyUserControl:LoadTime runat="server"  ID="MyUserControl"  />  <br />        <asp:Label ID="lblUserControlText" runat="server" />        <br />        <asp:Button ID="btn" runat="server"                    Text="Change Name to Jesse"                    OnClick="btn_Click" />  </div>     </form> </body> </html> 

Example 17-16. Default.aspx.cs for FragmentCachingWithProperty
 using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.Page {     protected void Page_Load(object sender, EventArgs e)     {        lblTime.Text = "This page was loaded at " +           DateTime.Now.ToLongTimeString(  );  lblUserControlText.Text = MyUserControl.UserName;  }  protected void btn_Click(object sender, EventArgs e)     {        MyUserControl.UserName = "Jesse";     }  } 

The FragmentCachingWithProperty example works fine when the page is first called, giving the result shown in Figure 17-4.

Figure 17-4. Results of FragmentCachingWithProperty

It will even work 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 in the browser, either by clicking the browser Refresh icon or clicking the Change Name button a third time, and ASP.NET attempts to satisfy the request for the user control from the cache, a server error occurs.

The only way around this is to move all the code that accesses the user control property into the user control itself. To see this, copy the FragmentCachingWithProperty example to a new web site, called FragmentCachingWithPropertyCorrect .

The calling page reverts back to the exact same page originally shown in Examples 17-11 and 17-12.

The user control picks up the Button control and the Click event handler for the button. The content and code-behind files for the user control are shown in Examples 17-17 and 17-18, respectively, with the changed code highlighted.

Example 17-17. SimpleUserControl.ascx for FragmentCachingWithPropertyCorrect
 <%@ Control Language="C#" AutoEventWireup="true"    CodeFile="SimpleUserControl.ascx.cs"    Inherits="SimpleUserControl_ascx" %> <%@ OutputCache Duration="10" VaryByParam="*" %> <hr /> <h3>User Control</h3> <asp:Label ID="lblTime" runat="server" /> <br /> <asp:Label ID="lblUserName" runat="server" Text="Dan" />  <br /> <asp:Button ID="btn" runat="server"    Text="Change Name to Jesse"    OnClick="btn_Click" />  <hr /> 

Example 17-18. SimpleUserControl.ascx.cs for FragmentCachingWithPropertyCorrect
 using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class SimpleUserControl_ascx : System.Web.UI.UserControl {    public string UserName    {       get       {          return lblUserName.Text;       }       set       {          lblUserName.Text = value;       }    }    protected void Page_Load(object sender, EventArgs e)     {        lblTime.Text = "This user control was loaded at " +           DateTime.Now.ToLongTimeString(  );     }  protected void btn_Click(object sender, EventArgs e)     {        lblUserName.Text = "Jesse";     }  } 

Though 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 will probably not be a good candidate for caching.



Programming ASP. NET
Programming ASP.NET 3.5
ISBN: 0596529562
EAN: 2147483647
Year: 2003
Pages: 173

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net