Previous Section  < Day Day Up >  Next Section

17.5. Caching

Consider a Web page with a list box containing the names of a thousand movie actors whose movies are displayed when an actor is selected. The first time the page is requested, a query is sent to the database, the list box is populated with the results, and the rendered HTML is returned to the user's browser. This is a relatively slow and expensive process. Multiply it by a thousand user requests and Web server response time will slow measurably.

One solution is to use caching to bypass most, or all, of this reprocessing by keeping an HTML page or data in memory. Subsequent requests for the page are handled using the cached information梠bviating the need to fetch data from the database. A sound caching strategy can improve Web server response more than any other single factor. This section presents the factors to be weighed in designing a caching strategy and the two broad categories of caching available in ASP.NET: output caching and data (or request) caching.

Page Output Caching

ASP.NET permits a developer to indicate whether all or part of a Web Form should be cached, where it should be cached, and how long it should remain in the cache before it expires and must be refreshed. The key to cache control for a Web Form is the @OutputCache directive:


<%@OutputCache Duration="30" Location="Any" 

   VaryByParam="none" %>


Its attributes declare the specific caching policies in effect for that Web page. Let's look at how these attributes are used.

Core Note

ASP.NET processes the @OutputCache directive by translating its attributes into HTTPCachePolicy method calls.


Specifying Cache Duration

The mandatory Duration attribute specifies the length, in seconds, that the page or control is cached. Setting this to 20 seconds generates the underlying statement:


Response.Cache.SetExpires(DateTime.Now.AddSeconds(20));


Specifying the Caching Location

The Location attribute specifies where the caching can occur. In general, caching occurs on a server, browser, or somewhere in between, such as on a proxy server. The values of this attribute correspond to these locations: Any, Client, DownStream, Server, ServerAndClient, or None. DownStream refers to a cache-capable device other than the origin server. None disables caching everywhere.

Conditional Caching with VaryByParam, VaryByHeader, and VaryByCustom

In Chapter 16, we developed a BMI calculator Web page in which the user enters a height and weight value and the BMI value is returned. Because there are thousands of combinations of height (htf, hti) and weight (wt) parameters, several slightly different versions of the same page are likely to be created. The VaryByParam attribute is available for cases such as this. Its value indicates the parameter(s) (from a POST or query string) that should be considered when selecting a page to cache. For example, the following statement causes a page to be cached for each unique combination of wt, hti, and htf values:


<%@ OutputCache Duration="60"  VaryByParam="wt;hti;htf" %>


Figure 17-7 shows four requests that result in three unique pages being cached with their calculated BMI value. Note that we could have also assigned an asterisk (*) to VaryByParam to indicate that each parameter's value affects the cache.

Figure 17-7. Cachedpage: VaryByParam="wt;hti;htf"


Be aware that you can create some unexpected results if you set VaryByParam incorrectly. If only the wt parameter were specified in this example, requests with a weight of 168 and heights of 5'1" and 6'1" would return the same Web page. The page returned to both would be the one requested earliest.

VaryByHeader caches a different version of a page, based on the value of the Request Header fields (refer to Figure 17-1). This is the most commonly used with the Accept-Language field to ensure that different language versions of a Web page are cached.

The final conditional attribute to be familiar with is VaryByCustom. This is most useful when you have a Web page that is rendered differently depending on the client's browser type and version. To create different page versions by browser type, set the VaryByCustom attribute to "Browser".


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

                               VaryByCustom="Browser" %>


This attribute also can be set to recognize custom values that your program generates. Please refer to caching documentation for details on this.

Caching a Partial Page (Fragment Caching)

All parts of a Web page are not created equally. Some, such as headers and links, rarely change, whereas other sections, such as the latest stock exchange quotes, change by the minute. A desirable caching strategy is to identify the static objects on the page and cache only those. This can be done in ASP.NET by creating custom controls (.ascx files) for the sections of the Web page to be cached. Custom controls have their own OutputCache directive that determines how they are cached, irrespective of the form containing them.

To illustrate the concept, let's create a simple control to be embedded within a Web page (see Figure 17-8). This segment simply contains a couple of links to other Web sites.


<!-- File: CacheFrag.ascx  -->

<%@ OutputCache Duration="30" VaryByParam="None" %>

<b>My Links</b><br>

<a href=http://www.moviesites.org>Movies</a><br>

<a href=http://www.imdb.com>Movie Reviews</a>

<br>


Figure 17-8. Example of custom control with its own caching policy


The application Web page to hold this control displays a title and date. Note that its caching is turned off by setting Location to None. The result is that only the control is cached.


<!-- File: CacheMain  -->

<%@ Page Language="C#"  %>

<%@ OutputCache Location="None" VaryByParam="*" %>

<%@ Register TagPrefix="frag" TagName="fraglinks" 

                Src="CacheFrag.ascx" %>

<HTML>

   <HEAD><TITLE>Sample Web Site</TITLE>

   <body>

      <center> <b>Fragment Sample</b>

      <br>

      <% Response.Output.Write(DateTime.Now.ToString("f")); %>

      </center>

      <frag:fraglinks  runat="server" />

   </body>

</html>


Data Caching

Data caching, sometimes referred to as request caching, is often an alternative to using application state or ViewState to store read-only data. It is as easy to use as the Application object, and adds the flexibility of assigning expiration and priority values to the stored data.

Adding Items to a Data Cache

The simplest way to add data to a data cache is to use the key-value syntax of the Cache property of the Page class:


Cache["userid"] = "rigoletto";


The Cache.Insert method is often a better approach since it takes full advantage of the fact that each cache entry is actually an instance of the CacheEntry class. This class contains many useful properties that are set by passing parameters to the Insert method:

Syntax:

Example:


public void Insert(

   string key,

   object value,

   CacheDependency dependencies,

   DateTime absoluteExpiration,

   TimeSpan slidingExpiration,

   CacheItemPriority priority,

   CacheItemRemovedCallBack cb

);



Cache.Insert (

   "userid", 

   "rigoletto",

   null,

   DateTime.Now.AddMinutes(20),

   TimeSpan.Zero,

   CacheItemPriority.Normal,

      null

);



Either the absoluteExpiration or slidingExpiration parameter can be used to determine when the cached data expires. The former sets a specific date and time; the latter sets the expiration to occur a specified amount of time after the value is last accessed. To disable sliding expiration, assign the value Cache.NoSlidingExpiration.

The dependencies parameter allows cached data to be tied to files, directories, or other objects in the cache. Establishing this dependency causes the cached data to be removed when the object it is dependent upon changes. Thus, if cached data comes from a file and the file contents are modified, the data in the cache is removed.


string filename = "actors";

CacheDependency dep = new CacheDependency(fileName);

cache.Insert("key", "value", dep);


The priority parameter makes sense when you understand that data in a cache is not guaranteed to remain there. Because there is no limit on what can be placed in the cache, ASP.NET periodically invokes a resource scavenging process to remove less important data from the cache. The determination of what is important is based on the priority of the resource. By setting this priority to a CacheItemPriority enum value, you can reduce, or increase, its likelihood of being removed through scavenging. Enum values may be set to AboveNormal, BelowNormal, Default, High, Low, Normal, and NotRemovable.

Retrieving Items from the Data Cache

To retrieve data from the cache, simply specify the key (case is ignored) of the desired data:


String user = (string) Cache["userid"];


Because the data is stored as an object, it is necessary to cast the result. If no data exists for the key, a null value is returned.

Data Caching as a Substitute for ViewState

The true value of data caching comes into play when large amounts of data need to be saved to restore a page's state. As was explained in the previous chapter, ViewState automatically saves data and state information for Web controls on a page as long as the page posts to itself. Figure 17-9 illustrates this. In it, we have Web page A containing a list box populated with several hundred items from a database. In steps 1 and 2, a user performs some action that results in a modified version of the same page being returned to the client. State is retained automatically because the contents of the page's list box梐nd any other fields梐re passed in the ViewState field.

Figure 17-9. Use a data cache to preserve information between separate pages


Suppose the user selects an item in the list box on page A that requires loading page B to show more details about the item. The server constructs the description as page B and returns it to the browser, discarding page A. After viewing this page, the user now wants to return to A. In the absence of caching, page A must be reconstructed by retrieving data from the database. To avoid this, the contents of the list box can be cached prior to navigating to page B (step 3):


// Step 3: Save listbox data before making server request 

Cache["listbox"]= actors.Items;


In step 6, page A is constructed using data from the cache to restore the list box.


// Step 6: Fill list box from cache

if(Cache["listbox"] !=null){

   ListItemCollection li= (ListItemCollection)Cache["listbox"];

   for(int i=0;i< li.Count; i++){

      ListItem a= li[i];

      actor.Items.Add(a);

   }

}

Cache["listbox"] = null;  // Clear cache


The cache is cleared after the page is restored; otherwise, both the cache and view state will contain the contents of the ListBox. It is worth noting that another option is to turn view state off for the ListBox (EnableViewState=false) and rely on caching to maintain its content.

Core Note

Data caching should be considered as a substitute for ViewState when data is read-only, or there is a large amount of it, or its update frequency is in minutes or hours rather than seconds, or when the data is not unique to individual users.


    Previous Section  < Day Day Up >  Next Section