Caching Application Pages and Data


The ASP.NET 3.5 Framework supports the following types of caching:
. Page Output Caching
. Partial Page Caching
. DataSource Caching
. Data Caching

Using Page Output Caching
You enable Page Output Caching by adding an <%@ OutputCache %> directive to a page. For example, the page in Listing caches its contents for 15 seconds.

LISTING CachePageOutput.aspx
<%@ Page Language=”C#” %>
<%@ OutputCache Duration=”15” VaryByParam=”none” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
void Page_Load()
{
lblTime.Text = DateTime.Now.ToString(“T”);
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Cache Page Output</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:Label id=”lblTime” Runat=”server” />
</div>
</form>
</body>
</html>

The page in Listing displays the current server time in a Label control. The page also includes an <%@ OutputCache %> directive. If you refresh the page multiple times, you will notice that the time is not updated until at least 15 seconds have passed. When you cache a page, the contents of the page are not regenerated each time you request the page.
The .NET class that corresponds to the page is not executed with each page request. The rendered contents of the page are cached for every user that requests the page. The page is cached in multiple locations. By default, the page is cached on the browser, any proxy servers, and on the web server. In Listing, the page is cached for 15 seconds. You can assign a much larger number to the duration attribute. For example, if you assign the value 86400 to the duration parameter, then the page is cached for a day. There is no guarantee that a page will be cached for the amount of time that you specify. When server memory resources become low, items are automatically evicted from the cache.

Varying the Output Cache by Parameter
Imagine that you need to create a separate master and details page. The master page displays a list of movies. When you click a movie title, the details page displays detailed information on the movie selected. When you create a master/details page, you typically pass a query string parameter between the master and details page to indicate the particular movie to display in the details page. If you cache the output of the details page, however, then everyone will see the first movie selected. You can get around this problem by using the VaryByParam attribute. The VaryByParam attribute causes a new instance of a page to be cached when a different parameter is passed to the page. (The parameter can be either a query string parameter or a form parameter.)

Varying the Output Cache by Control
The VaryByControl attribute enables you to generate different cached versions of a page depending on the value of a particular control in the page. This attribute is useful when you need to create a single-page Master/Details form.

Varying the Output Cache by Header
Another option is to use the VaryByHeader attribute to create different cached versions of a page when the value of a particular browser header changes. Several standard browser headers are transmitted with each page request, including

. Accept-Language—Represents a prioritized list of languages that represent the preferred human language of the user making the request.
. User-Agent—Represents the type of device making the request.
. Cookie—Represents the browser cookies created in the current domain.

Varying the Output Cache by Browser
A better way to create different cached versions of a page that depend on the type of browser being used to request the page is to use the VaryByCustom attribute. This attribute accepts the special value browser. When VaryByCustom has the value browser, only two attributes of the browser are considered important: the type of browser and its major version. For example, a page request from Internet Explorer results in a different cached version of the page than does one from Firefox. A page request from Internet Explorer 5 rather than Internet Explorer 6.5 also results in a different cached version. Any other variations in the User-Agent header are ignored.

Varying the Output Cache by a Custom Function
The VaryByCustom attribute is named the VaryByCustom attribute for a reason. You can specify a custom function that determines when a different cached version of a page is generated. You can use any criteria that you please with the custom function. You can create different cached versions of a page depending on the browser minor version, the browser DOM support, the time of day, or even the weather.

Specifying the Cache Location
You can use the Location attribute of the <%@ OutputCache %> directive to specify where a page is cached. This attribute accepts the following values:

. Any—The page is cached on the browser, proxy servers, and web server (the default value).
. Client—The page is cached only on the browser.
. Downstream—The page is cached on the browser and any proxy servers but not the web server.
. None—The page is not cached.
. Server—The page is cached on the web server but not the browser or any proxy servers.
. ServerAndClient—The page is cached on the browser and web server, but not on any proxy servers.

Using Partial Page Caching
In the previous section of this chapter, you learned how to cache the entire output of a page. In this section, you learn how to take advantage of Partial Page Caching to cache  particular regions of a page. Partial Page Caching makes sense when a page contains both dynamic and static content. For example, you might want to cache a set of database records displayed in a page, but not cache a random list of news items displayed in the same page.

Using Post-Cache Substitution
In some cases, you might want to cache an entire page except for one small area. For example, you might want to display the current username dynamically at the top of a page but cache the remainder of a page. In these cases, you can take advantage of a feature of the ASP.NET Framework called post-cache substitution. Post-cache substitution is used internally by the AdRotator control. Even when you use Page Output Caching to cache a page that contains an AdRotator control, the content rendered by the AdRotator control is not cached. You can use post-cache substitution either declaratively or programmatically. If you want to use post-cache substitution declaratively, then you can use the ASP.NET Substitution control.

Caching with a User Control
Using post-cache substitution is appropriate only when working with a string of text or HTML. If you need to perform more complex partial page caching, then you should take advantage of User Controls. You can cache the rendered contents of a User Control in memory in the same way as you can cache an ASP.NET page. When you add an <%@ OutputCache %> directive to a User Control, the rendered output of the User Control is cached. When you cache a User Control, the content is cached on the web server and not on any proxy servers or web browsers. When a web browser or proxy server caches a page, it always caches an entire page.

Sharing a User Control Output Cache
By default, instances of the same User Control located on different pages do not share the same cache. For example, if you add the same Movies User Control to more than onepage, then the contents of each user control is cached separately. If you want to cache the same User Control content across multiple pages, then you need to include the Shared attribute when adding the <%@ OutputCache %> directive to a User Control.

Manipulating a User Control Cache Programmatically
When you include an <%@ OutputCache %> directive in a User Control, you can modify programmatically how the User Control is cached. The User Control CachePolicy property exposes an instance of the ControlCachePolicy class, which supports the following properties:

 Cached—Enables you to enable or disable caching.
 Dependency—Enables you to get or set a cache dependency for the User Control.
 Duration—Enables you to get or set the amount of time (in seconds) that content is cached.
 SupportsCaching—Enables you to check whether the control supports caching.
 VaryByControl—Enables you to create different cached versions of the control, depending on the value of a control.
VaryByParams—Enables you to create different cached versions of the control, depending on the value of a query string or form parameter.

The ControlCachePolicy class also supports the following methods:
 SetExpires—Enables you to set the expiration time for the cache.
 SetSlidingExpiration—Enables you to set a sliding expiration cache policy.
 SetVaryByCustom—Enables you to specify a custom string used by a custom cache policy. (You also can supply the special value browser, which causes different cached versions of the control to be created when the type and major version of the browser differs.)

Creating a User Control Cache File Dependency
You can use the CacheControlPolicy.Dependency property to create a dependency between a cached User Control and a file (or set of files) on the file system. When the file is modified, the User Control is dropped from the cache automatically and reloaded with the next page request.

Caching Dynamically Loaded User Controls
You can load a User Control dynamically by using the Page.LoadControl() method. You can cache dynamically loaded User Controls in the same way that you can cache User Controls declared in a page. If a User Control includes an <%@ OutputCache %> directive, then the User Control will be cached regardless of whether the control was added to a page declaratively or programmatically. However, you need to be aware that when a cached User Control is loaded dynamically, the ASP.NET Framework automatically wraps the User Control in an instance of the PartialCachingControl class. Therefore, you need to cast the control returned by the Page.LoadControl() method to an instance of the PartialCachingControl class

Using DataSource Caching
Instead of caching at the page or User Control level, you can cache at the level of a DataSource control. Three of the four standard ASP.NET DataSource controls—the SqlDataSource, ObjectDataSource, and XmlDataSource controls—include properties that enable you to cache the data that the DataSource control represents (The LinqDataSource control does not support caching). One advantage of using the DataSource controls when caching is that the DataSource controls can reload data automatically when the data is updated. For example, if you use a SqlDataSource control to both select and update a set of database records, then the SqlDataSource control is smart enough to reload the cached data after an update. The DataSource controls are also smart enough to share the same data across multiple pages. For example, when using the SqlDataSource control, a unique entry is created in the Cache object for each combination of the following SqlDataSource properties: SelectCommand, SelectParameters, and ConnectionString. If these properties are identical for two SqlDataSource controls located on two different pages, then the two controls share the same cached data.

Using a Sliding Cache Expiration Policy
If you need to cache a lot of data, then it makes more sense to use a sliding expiration policy rather than an absolute expiration policy. When you use a sliding expiration policy, data remains in the cache as long as the data continues to be requested within a certain interval. For example, imagine that you have been asked to rewrite the Amazon website with ASP.NET. The Amazon website displays information on billions of books. You couldn’t cache all this book information in memory. However, if you use a sliding expiration policy, then you can cache the most frequently requested books automatically.

Caching with the ObjectDataSource Control
The ObjectDataSource control supports the same caching properties as the SqlDataSource control. You can cache the data that an ObjectDataSource control represents by setting its EnableCaching, CacheDuration, and (optionally) CacheExpirationPolicy properties. Multiple ObjectDataSource controls can share the same cached data. To share the same cache, the ObjectDataSource controls must have identical TypeName, SelectMethod, and SelectParameters properties.

Caching with the XmlDataSource Control
Unlike the SqlDataSource and ObjectDataSource controls, the XmlDataSource control has caching enabled by default. The XmlDataSource automatically creates a file dependency on the XML file that it represents. If the XML file is modified, the XmlDataSource control automatically reloads the modified XML file.

Using Data Caching
Behind the scenes, all the various caching mechanisms included in the ASP.NET Framework use the Cache object. In other words, the Cache object is the fundamental mechanism for all caching in the ASP.NET Framework. One instance of the Cache object is created for each ASP.NET application. Any items you add to the cache can be accessed by any other page, control, or component contained in
the same application (virtual directory). In this section, you learn how to use the properties and methods of the Cache object. You learn how to add items to the cache, set cache expiration policies, and create cache item dependencies.

Using the Cache Application Programming Interface
The Cache object exposes the main application programming interface for caching. This object supports the following properties:
 Count—Represents the number of items in the cache.
 EffectivePrivateBytesLimit—Represents the size of the cache in kilobytes.

The Cache object also supports the following methods:
 Add—Enables you to add a new item to the cache. If the item already exists, this method fails.
 Get—Enables you to return a particular item from the cache.
 GetEnumerator—Enables you to iterate through all the items in the cache.
 Insert—Enables you to insert a new item into the cache. If the item already exists, this method replaces it.
 Remove—Enables you to remove an item from the cache.

Adding Items to the Cache
You can add items to the cache by using the Insert() method. There are several overloaded versions of the Insert() method. The maximally overloaded version of the Insert() method accepts the following parameters:

 key—Enables you to specify the name of the new item.
 value—Enables you to specify the value of the new item.
 dependencies—Enables you to specify one or more cache dependencies, such as a file, key, or SQL dependency.
 absoluteExpiration—Enables you to specify an absolute expiration time for the cached item. If you don’t need to specify a value for this property, use the static field Cache.NoAbsoluteExpiration.
 slidingExpiration—Enables you to specify a sliding expiration interval for the cached item. If you don’t need to specify a value for this property, use the static field Cache.NoSlidingExpiration.
 priority—Enables you to specify the priority of the cached item. Possible values are AboveNormal, BelowNormal, Default, High, Low, Normal, and NotRemovable.
 onRemoveCallback—Enables you to specify a method that is called automatically before the item is removed from the cache.

Adding Items with an Absolute Expiration Policy
When you insert items in the cache, you can specify a time when the item will expire. If you want an item to remain in the cache for an extended period of time, then you should always specify an expiration time for the item.

Adding Items with a Sliding Expiration Policy
When you specify a sliding expiration policy, items remain in the cache just as long as they continue to be requested within a specified interval of time. For example, if you specify a sliding expiration policy of 5 minutes, then the item remains in the Cache just as long as no more than 5 minutes pass without the item being requested. Using a sliding expiration policy makes sense when you have too many items to add to the cache. A sliding expiration policy keeps the most requested items in memory and the remaining items are dropped from memory automatically.

Adding Items with Dependencies
When you add an item to the Cache object, you can make the item dependent on an external object. If the external object is modified, then the item is automatically dropped from the cache.

Specifying Cache Item Priorities
When you add an item to the Cache, you can specify a particular priority for the item. Specifying a priority provides you with some control over when an item gets evicted from the Cache. For example, you can indicate that one cached item is more important than other cache items so that when memory resources become low, the important item is not evicted as quickly as other items.

Configuring the Cache
You can configure the size of the cache by using the web configuration file. You specify cache settings with the cache element. This element supports the following attributes:

disableMemoryCollection—Enables you to prevent items from being removed from the cache when memory resources become low.
disableExpiration—Enables you to prevent items from being removed from the cache when the items expire.
privateBytesLimit—Enables you to specify the total amount of memory that can be consumed by your application and its cache before items are removed.
percentagePhysicalMemoryUsedLimit—Enables you to specify the total percentage of memory that can be consumed by your application and its cache before items are removed.
privateBytesPollTime—Enables you to specify the time interval for checking the application’s memory usage.

Using SQL Cache Dependencies
One of the most powerful features supported by the ASP.NET Framework is SQL cache dependencies. This feature enables you to reload cached database data automatically whenever the data in the underlying databases changes. There is a tradeoff when you use either an absolute or sliding cache expiration policy. The tradeoff is between performance and stale data. For example, if you cache data in memory for 20 seconds, then the data that is displayed on your web pages might be 20 seconds out of date. In the case of most applications, displaying slightly stale data does not really matter. For example, if you are building a discussion forum, then everyone can live with the fact that new posts might not appear immediately. However, there are certain types of applications in which you cannot afford to display any stale data at all. For example, if you are creating a stock trading website or an auction website, then every second might count. The ASP.NET Framework’s support for SQL cache dependencies enables you to take advantage of caching but minimize stale data. When you use a SQL cache dependency, you can automatically detect when data has changed in the underlying database and refresh the data in the cache.

The ASP.NET Framework supports two types of SQL cache dependencies: Polling and Push. You can use Polling SQL cache dependencies with any recent version of Microsoft SQL Server, including Microsoft SQL Server 2005 Express, Microsoft SQL Server 2000, and Microsoft SQL Server 7.0. The second type of cache dependency, Push SQL cache dependency, works with only Microsoft SQL Server 2005 or Microsoft SQL Server 2005 Express because it requires the SQL Server 2005 Service Broker. You can use either type of SQL cache dependencies with Page Output Caching, DataSource Control Caching, and Data Caching. The following sections examine each scenario.

Using Polling SQL Cache Dependencies
A Polling SQL cache dependency is the most flexible type of SQL cache dependency, and I recommend that you use Polling rather than Push SQL cache dependencies for most applications. You can use a Polling SQL cache dependency to detect any type of modification to a database table. Behind the scenes, a Polling SQL cache dependency uses a database trigger. When a table is modified, the trigger fires and a row in a database table named AspNet_SqlCacheTablesForChangeNotification is updated to record the fact that the table has been changed. The ASP.NET Framework uses a background thread to poll this database table for changes on a periodic basis. If there has been a change, then any item in the cache that is dependent on the database table is dropped from the cache.

Using Push SQL Cache Dependencies
When using Microsoft SQL Server 2005, you have the option of using Push SQL cache dependencies rather than Polling SQL cache dependencies. Microsoft SQL Server 2005 includes a feature called query notifications, which use the Microsoft SQL Server 2005 Service Broker in the background. The Service Broker can automatically send a message to an application when data changes in the database.

You can create two types of databases with SQL Server Express: a Local or a Server database. You should not use Push dependencies with a Local database. You should use Push dependencies only with a Server database. You cannot create new Server databases when using Visual Web Developer. You can create a Server database by using the full version of Visual Studio 2008 or by downloading Microsoft SQL Server Management Studio Express from the Microsoft MSDN website (msdn.microsoft.com). 

No comments:

Post a Comment