ASP.NET has powerful built-in caching capabilities that you can access through the cache object’s familiar and intuitive "key and item" data access model. Using this model, it’s natural to write code that uses the cache object as you would a collection, searching for cached data by keys and relying on the absence of a key to indicate that you need to retrieve the data from a data store. More advanced developers encapsulate the caching behavior into whatever data retrieval methods they write, but they still rely on the missing key condition to determine whether their code needs to retrieve the data or return the cached copy.
However, there is an elegant technique for data caching that leverages the common asynchronous programming patterns used in the .NET Framework. Using this technique, you’ll be able to make the caching engine tell you when you need to retrieve your expired data.
Delegating your caching authority
To use this technique, you’ll first need to declare a delegate variable of type CacheItemRemovedCallback, as in the following C# fragment:
CacheItemRemovedCallback onRemove = null;
Delegates are simply type-safe function pointers used to store the address of a method that can later be executed. They’re widely used in asynchronous programming, or in any situation where loosely coupled communication is desirable, such as event-handling code. For more information on delegates, I’d recommend reading “Simplify .NET class communication with delegates.”
Next, create a method with an appropriate signature for the CacheItemRemovedCallback delegate where you’ll place the code to retrieve whatever data you’ve cached:
public void RemovedProducts(string k, Object v, CacheItemRemovedReason r)
// Go get the data again
You’ll notice that this callback method accepts the key of the item that was removed, the item itself, and an argument that indicates the reason the item was removed from the cache. These reasons are encapsulated within the CacheItemRemovedReason enum and include Expired, Removed, Underused, and DependencyChanged. While the first two of these arguments need no explanation, the third may occur if ASP.NET needs to recover the memory and flushes the item from the cache. I will discuss the fourth one in the next section.
The final step is to declare an instance of the CacheItemRemovedCallback delegate and pass it to the Insert method when an item is added to the cache. To cache a DataTable named dt with akey value of Products and set a six-hour expiration, you could use the following code:
onRemove = new CacheItemRemovedCallback(this.RemovedProducts);
this.Cache.Insert("Products", dt, null,
The caching engine will then call the delegate function RemovedProducts whenever the value associated with the Products key is removed for whatever reason. If the DataTable were removed from the cache because it had expired, then RemovedProducts would be called with a removal reason argument of Expired, which would be your cue to refresh the data and recache it.
Tracking cached dependent objects
As hinted at by the DependencyChanged member of the CacheItemRemovedCallback enumeration, the ASP.NET caching engine can track dependencies between cached items and other elements. Dependencies can be formed between two items in the cache or between a cached item and a file system object such as a file, a directory, or an array of either. For example, the following VB.NET snippet sets up a dependency between an item in the cache referred to as Parent and an item called Child:
Me.Cache("Parent") = "parent data"
Dim dependencyKey(0) As String dependencyKey(0) = "Parent"
Dim dep As new CacheDependency(Nothing, dependencyKey)
Me.Cache.Insert("Child", "child data", dep)
When the Parent item is removed from the cache, the Child item is removed as well. Combine this with the delegate mechanism I introduced above, and you can have a sophisticated caching system consisting of entire groups of related objects that can automatically refresh themselves when they expire from your application’s cache.