Cache and the Application API


Cache and the Application API

The output cache and partial page output cache both rely upon common infrastructure for their storage of output cached data. This common infrastructure is the Cache API, which is responsible for managing dependencies and the memory used by all caching features.

The Cache API is a dictionary-based API, which means we can use string keys to set and retrieve items similar to Session or Application. If you are familiar with programming Session or Application, you will understand the Cache API. For example, to store a DataSet in Session, Application, and Cache, you would use the code in Code Listing 6-3.

Code Listing 6-3: Session, Application, and Cache

start example
 // Name a common key 
string key = "ProductsKey";

// Use some custom business logic class to create the DataSet
//
DataSet products = ProductsDB.GetProductsDataSet();

// Store in the user’s Session – available on for this user
//
Session[key] = products;

// Store in Application and Cache – available to entire application
//
Application[key] = products;
Cache[key] = products;
end example

Storing the products DataSet in Session stores a copy of the DataSet for each user for which that Session[key] is called. Additionally, as we discussed in Chapter 5, you can configure Session such that all servers in the Web farm have access to a common session store.

Note

We’re using a DataSet for consistency in the sample code. If you are using out-of-process Session, store only as little information as is required. Storing an entire DataSet for each user can get expensive quickly, especially if you have a very active site.

Storing the products DataSet in Application or Cache stores a copy of the DataSet for all users in the memory space of the current application (this data is isolated from any other running applications)—if the user browses to another server in the server farm, that server has the responsibility to either execute code or fetch the item from its Cache or Application. An item stored in Cache or Application is not replicated between servers in the server farm, and neither of these features supports an out-of-process mode similar to Session.

Note

Why doesn’t ASP.NET provide a common Application or Cache out-of-process option similar to Session? Unlike Session, which is tied to a specific user, Application and Cache contain application-wide settings that apply, or are available to, all users. Thus, changes to Application or Cache must be propagated immediately. However, there are several problems with this: managing contentions when two applications simultaneously modify the same data; and decreased efficiency with replication—as the number of servers grows, the data gets exponentially more difficult to replicate between servers.

Deciding to Use Cache or Application

Application and Cache both store data locally in the memory of the running application. Both, in effect, provide identical functionality. So the question is which to use. It is recommended that any time you need to store application- wide data, use Cache instead of Application.

Note

Cache supersedes all the functionality provided by Application and both simplifies it (because it requires no locking to modify) and provides more advanced functionality (such as expiration, dependencies, and purging of data when necessary). This chapter does not contain any direct discussion of Application. Application exists primarily for backward compatibility with Microsoft Active Server Pages, or ASP.

Here is a summary of Cache functionality:

  • Automatic eviction of infrequently used items

  • Pinning items in the cache to prevent automatic eviction

  • Method callback when an item is removed from the cache

  • Cache dependencies to control evictions

What to Store in the Cache

We commonly are asked, “what should be stored in the cache?” The answer really depends on what the application is doing. Common objects such as DataSets are excellent objects to store in the cache because they can be used as is for data binding and other scenarios. Any custom class that represents data is also a good candidate. Take a look at the source code for the ASP.NET Forums (http://www.asp.net/Forums/). We have business classes that represent forums, forum groups, threads, posts, and so on, many of which are stored and retrieved from the cache for anonymous user requests—for known users we don’t cache, but for anonymous users we do cache.

We should also address what should not be stored in the cache. An item such as a DataReader is an excellent example of what should not be stored in the cache. A DataSet and a DataReader are both classes provided by ADO.NET and used for working with data. A DataSet is a disconnected snapshot of the data, whereas a DataReader holds a cursor in the database. A DataReader shouldn’t be stored in the cache since the DataReader has an open connection to the database. Storing a reference to a DataReader instance would keep that connection open, and keeping the connection open prevents it from being sent back to the connection pool. Eventually, all available connections would be used and you would start to get exceptions.

Setting and Retrieving Values from the Cache

Adding and retrieving items from the cache is easy. You can accomplish both in several ways, the most common of which is using the dictionary-style API, shown in Code Listing 6-4.

Code Listing 6-4: Cache Dictionary API

start example
 // Name a common key 
string key = "ProductsKey";

// Use some custom business logic class to create the DataSet
DataSet products = ProductsDB.GetProductsDataSet();

// Add the DataSet to the Cache
Cache[key] = products;

// Get a DataSet ‘products’ from the Cache
DataSet ds = (DataSet) Cache[key];
end example

In addition to using the dictionary-style API, which is easy to work with but doesn’t present the full capabilities of the cache, you can store items in the cache using two methods: Insert and Add.

You use these methods slightly differently. The Insert method stores an item in the cache and replaces an existing named item if it already exists. The Add method stores an item in the cache but doesn’t replace an existing item.

Inserting an Object into the Cache

The Insert method is overloaded and has four signatures.

  • Insert(string key, object value)

  • Insert(string key, object value, CacheDependency dependency)

  • Insert(string key, object value,
    CacheDependency dependency,
    DateTime absoluteExpiration,
    TimeSpan slidingExpiration)

  • Insert(string key, object value,
    CacheDependency dependency,
    DateTime absoluteExpiration,
    TimeSpan slidingExpiration,
    CacheItemPriority priority,
    CacheItemRemovedCallback callback)

The Insert(string key, object value) overload is identical in behavior to the dictionary-style API. It accepts a key or a name of the cache item to add as well as an object type for the item to store.

Inserting an item with a dependency into the cache

The second Insert method implementation also accepts a CacheDependency parameter This parameter indicates that the item added to the cache will be dependent upon external conditions. When these external conditions change, the item is automatically removed from the cache. These external conditions are configured through the CacheDependency class.

The CacheDependency class is used to make Cache entries dependent upon either files or other cache entries. An instance of this class is created using one of the eight constructors it supports:

  • CacheDependency(string filename)

  • CacheDependency(string[] filenames)

  • CacheDependency(string filename, DateTime start)

  • CacheDependency(string[] filenames, DateTime start)

  • CacheDependency(string[] filenames, string[] cachekeys)

  • CacheDependency(string[] filenames,
    string[] cachekeys, DateTime start)

  • CacheDependency(string[] filenames,
    string[] cachekeys,
    CacheDependency dependency)

  • CacheDependency(string[] filenames,
    string[] cachekeys,
    CacheDependency dependency, DateTime start)

Using the constructors in the preceding list, we can create CacheDependency objects for a file or files. Here is the code for file dependency with a single file:

// Create the dependency
CacheDependency c = new CacheDependency(Server.MapPath("products.xml"));

// Insert into the Cache
Cache.Insert("products", productsDataSet, c);

Here is the code for multiple file dependency:

// Create the files we are dependent upon
String[] files = new String[2];
files[0] = Server.MapPath("products.xml");
files[1] = Server.MapPath("sales.xml");

// Create the dependency
CacheDependency c = new CacheDependency(files);

// Insert into the Cache
Cache.Insert("products", productsDataSet, c);

In addition to monitoring files, we can create CacheDependency objects for existing Cache keys. Dependencies that reply upon existing Cache entries are very powerful because we can essentially extend the cache to support a dependency of our choice. For example, when a manager updates the sales history, we can remove the Sales cache item, which can remove related and dependent items through cascading. Following is an example single key dependency:

// Keys we are dependent upon
String[] keys = new String[1];
keys[0] = "Sales";

// Create the dependency
CacheDependency c = new CacheDependency(null, keys);

// Insert into the Cache
Cache.Insert("products", productsDataSet, c);

Here is an example of code for multiple key dependency:

// Keys we are dependent upon
String[] keys = new String[2];
keys[0] = "Sales";
keys[1] = "Products";

// Create the dependency
CacheDependency c = new CacheDependency(null, keys);

// Insert into the Cache
Cache.Insert("products", productsDataSet, c);

We can also specify when we want monitoring of the files to start by using the DateTime start parameter:

// Create the dependency
CacheDependency c = new CacheDependency(Server.MapPath("products.xml"),
DateTime.Now.AddSeconds(15));

// Insert into the Cache
Cache.Insert("products", productsDataSet, c);

Delayed monitoring of the file for changes is a nice feature—since we know that operations on the file will occur within a certain period of time, we can delay any removal from cache until past the time specified.

Lastly, CacheDependency also provides a way to aggregate a new dependency with an existing dependency. Aggregate dependency basically means this: “The following dependency is dependent upon these conditions plus the included existing dependency.” Aggregate dependency is shown here:

// ... early in the code execution
// Create the dependency
CacheDependency d = new CacheDependency(Server.MapPath("products.xml"),
DateTime.Now.AddSeconds(15));

// ... later in the code execution
// Keys we are dependent upon
String[] keys = new String[1];
keys[0] = "Sales";

// Create the key dependency
CacheDependency c = new CacheDependency(null, keys, d);

// Insert into the Cache
Cache.Insert("products", productsDataSet, c);

Removing items from the Cache at a point in time

The fourth overloaded Insert method allows us to control time-based eviction of items from the cache using a DateTime parameter and a TimeSpan parameter:

Insert(string key, object value, 
CacheDependency dependency,
DateTime absoluteExpiration,
TimeSpan slidingExpiration)

Scavenger hints and the removal callback

The final overloaded method for Insert lets us set a CacheItemPriority parameter and a CacheItemRemovedCallback parameter:

Insert(string key, object value, 
CacheDependency dependency,
DateTime absoluteExpiration,
TimeSpan slidingExpiration,
CacheItemPriority priority,
CacheItemRemovedCallback callback)

The CacheItemPriority parameter allows us to instruct the cache as to which priority the inserted item should receive. For example, underused items within the Cache with a low priority are the first to be evicted when ASP.NET is under memory pressure. CacheItemPriority is an enumeration. Its values are shown in Table 6-4, in order of precedence:

Table 6-4: CacheItemPriority Values, in Priority Order

Member Name

Description

Low

First items to be removed.

BelowNormal

Items that are removed after low priority items.

Normal

Default priority when the priority is not specified. This is the default used by output caching.

AboveNormal

More important than regular items.

High

Last items to be removed.

NotRemovable

Items that cannot be removed. Don’t use this option unless you are fully confident that the item has to remain in memory no matter what load the server is under. Configuration data is a good example for this value.

How should these priorities be used? If you have multiple, large, but easily created items stored in the cache, assigning them a Low priority would ensure that the items would be the first evicted. Conversely, if you had an item that was small but very expensive to create, you would want to assign it a High priority so that it would be evicted only under extreme circumstances.

The last parameter of Insert is an instance of CacheItemRemovedcallback. It allows us to pass in a delegate method that is called when an item is removed from the cache.

Finally, for items that should never be removed from the cache—and to mirror the behavior of Application—use the NotRemovable value. Items added to the cache with NotRemovable will not be removed from the cache unless code is used to remove them, or they have a dependency that is enforced.

Add Method

The Add method accepts the same parameters as the last overloaded Insert method. The only difference is that Insert always replaces an existing Cache item and has no return value, and Add will not only insert an item into the cache if it doesn’t already exist but also return the item from the cache.

Removing Items from the Cache

Entries within the cache can be removed by using the Remove method:

// Remove a key named ‘products’
Cache.Remove("products");




Microsoft ASP. NET Coding Strategies with the Microsoft ASP. NET Team
Microsoft ASP.NET Coding Strategies with the Microsoft ASP.NET Team (Pro-Developer)
ISBN: 073561900X
EAN: 2147483647
Year: 2005
Pages: 144

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