Preserved for
backward compatibility with classic ASP applications, the Application
intrinsic object presents itself as a global container of data with an
indexer property and a user-callable locking mechanism. The Cache
object is a smarter and thread-safe container that can automatically
remove unused items, support various forms of dependencies, and
optionally provide removal callbacks and priorities.
The Application object is maintained for backward compatibility with legacy applications; new ASP.NET applications should use the Cache object.
1. The Cache Class
The Cache class is exposed by the System.Web.Caching namespace and is a new entry in the set of tools that provide state management in ASP.NET. The Cache
class works like an applicationwide repository for data and objects,
but this is the only aspect that it has in common with the HttpApplicationState class, as we’ll see in a moment.
An instance of the Cache
class is created on a per-AppDomain basis and remains valid until that
AppDomain is up and running. The current instance of the application’s
ASP.NET cache is returned by the Cache property of the HttpContext object or the Cache property of the Page object.
Cache and Other State Objects
In spite of their common goal—to serve as a global data repository for ASP.NET applications—Cache and HttpApplicationState classes are quite different. Cache
is a thread-safe object and does not require you to explicitly lock and
unlock before access. All critical sections in the internal code are
adequately protected using synchronization constructs. Another key
difference with the HttpApplicationState class is that data stored in Cache doesn’t necessarily live as long as the application does. The Cache object lets you associate a duration as well as a priority with any of the items you store.
Any cached item can be configured to expire
after a specified number of seconds, freeing up some memory. By setting a
priority on items, you help the Cache
object select which items can be safely disposed of in case of memory
shortage. Items can be associated with various types of dependencies,
such as the timestamp of one or more files and directories, changes on
other cached items, database table changes, and external events. When
something happens to break the link, the cached item is invalidated and
is no longer accessible by the application.
Both Cache and HttpApplicationState
are globally visible classes and span all active sessions. However,
neither works in a Web farm or Web garden scenario; in general, they
don’t work outside the current AppDomain.
Note
When more than one
AppDomain is involved (for example, in a Web farm), presumably all
AppDomains would contain the same cached data, assuming that the cached
information is not dynamic. Unlike with session state, this isn’t as
troubling because the assumption is that applicationwide static values
can be read upon initialization and cache timeout. If the cached
information is dynamic, that’s a different story. In that case, you
should consider a global cross-machine container, as we’ll discuss
shortly. |
The Cache
object is unique in its capability to automatically scavenge the memory
and get rid of unused items. Aside from that, it provides the same
dictionary-based and familiar programming interface as Application and Session. Unlike Session, the Cache
class does not store data on a per-user basis. Furthermore, when the
session state is managed in-process, all currently running sessions are
stored as distinct items in the ASP.NET Cache.
Note
If you’re looking for a global repository object that, like Session,
works across a Web farm or Web garden architecture, you might become
frustrated. No such object exists in the .NET Framework. To build a
cross-machine container, you need to resort to a shared and remote
resource, such as an external service or perhaps an installation of
Microsoft SQL Server or another database. This means that each access to
data will require serialization and is subject to network latency. In
general, this scheme is complex enough to invalidate most of the
advantages you get from data caching. As far as caching is involved, the
tradeoff to evaluate is accessing ready-made data versus running the
query to fetch a fresh copy of desired data. ASP.NET provides an
effective infrastructure for caching data locally because that is what
you need most of the time. Adding to the infrastructure to cover farms
is up to you. |
Properties of the Cache Class
The Cache
class provides a couple of properties and public fields. The properties
let you count and access the various items. The public fields are
internal constants used to configure the expiration policy of the cache
items. Table 1 lists and describes them all.
Table 1. Cache Class Properties and Public Fields
Property | Description |
---|
Count | Gets the number of items stored in the cache |
Item | An indexer property that provides access to the cache item identified by the specified key |
NoAbsoluteExpiration | A static constant that indicates a given item will never expire |
NoSlidingExpiration | A static constant that indicates sliding expiration is disabled for a given item |
The NoAbsoluteExpiration field is of the DateTime type and is set to the DateTime.MaxValue date—that is, the largest possible date defined in the .NET Framework. The NoSlidingExpiration field is of the TimeSpan type and is set to TimeSpan.Zero, meaning that sliding expiration is disabled. We’ll say more about sliding expiration shortly.
The Item
property is a read/write property that can also be used to add new items
to the cache. If the key specified as the argument of the Item property does not exist, a new entry is created. Otherwise, the existing entry is overwritten.
The data stored in the cache is generically considered to be of type object, whereas the key must be a case-sensitive string. When you insert a new item in the cache using the Item
property, a number of default attributes are assumed. In particular,
the item is given no expiration policy, no remove callback, and a normal
priority. As a result, the item will stay in the cache indefinitely,
until programmatically removed or until the application terminates. To
specify any extra arguments and exercise closer control on the item, use
the Insert method of the Cache class instead.
Methods of the Cache Class
The methods of the Cache class let you add, remove, and enumerate the items stored. Methods of the Cache class are listed and described in Table 2.
Table 2. Cache Class Methods
Method | Description |
---|
Add | Adds
the specified item to the cache. It allows you to specify dependencies,
expiration and priority policies, and a remove callback. The call fails
if an item with the same key already exists. The method returns the
object that represents the newly added item. |
Get | Retrieves the value of the specified n item from the cache. The item is identified by key. The method returns null if no item with that key is found. (This method is used to implement the get accessor of the Item property.) |
GetEnumerator | Returns a dictionary enumerator object to iterate through all the valid items stored in the cache. |
Insert | Inserts the specified item into the cache. Insert
provides several overloads and allows you to specify dependencies,
expiration and priority policies, and a remove callback. The method is
void and, unlike Add, overwrites an existing item having the same key as the item being inserted. (This method is used to implement the set accessor of the Item property.) |
Remove | Removes
the specified item from the cache. The item is identified by the key.
The method returns the instance of the object being removed or null if no item with that key is found. |
Both the Add and Insert
methods don’t accept null values as the key or the value of an item to
cache. If null values are used, an exception is thrown. You can
configure sliding expiration for an item for no longer than one year.
Otherwise, an exception will be raised. Finally, bear in mind that you
cannot set both sliding and absolute expirations on the same cached
item.
Note
Add and Insert work in much the same way, but a couple of differences make it worthwhile to have both on board. Add fails (but no exception is raised) if the item already exists, whereas Insert overwrites the existing item. In addition, Add has just one signature, while Insert provides several different overloads. |
An Interior View
The Cache class inherits from Object and implements the IEnumerable
interface. It is a wrapper around an internal class that acts as the
true container of the stored data. The real class used to implement the
ASP.NET cache varies depending on the number of affinitized CPUs. If
only one CPU is available, the class is CacheSingle; otherwise, it is CacheMultiple. In both cases, items are stored in a hashtable and there will be a distinct hashtable for each CPU. It turns out that CacheMultiple manages an array of hashtables. Figure 1 illustrates the architecture of the Cache object.
The hashtable is divided into two parts—public
and private elements. In the public portion of the hashtable are placed
all items visible to user applications. System-level data, on the other
hand, goes in the private section. The cache is a resource extensively
used by the ASP.NET runtime itself; system items, though, are neatly
separated by application data and there’s no way an application can
access a private element on the cache.
The Cache object is mostly a way to restrict applications to read from, and write to, the public segment of the data store. Get and set methods on internal cache classes accept a flag to denote the public attribute of the item. When called from the Cache class, these internal methods always default to the flag that selects public items.
The hashtable containing data is then enhanced
and surrounded by other internal components to provide a rich set of
programming features. The list includes the implementation of a least
recently used (LRU) algorithm to ensure that items can be removed if the
system runs short of memory, dependencies, and removal callbacks.
Caution
On
a multiprocessor machine with more than one CPU affinitized with the
ASP.NET worker process, each processor ends up getting its own Cache
object. The various cache objects are not synchronized. In a Web garden
configuration, you can’t assume that users will return to the same CPU
(and worker process) on subsequent requests. So the status of the
ASP.NET cache is not guaranteed to be aligned with what the same page
did last time. |