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).