.NET Data Caching, Part 2
By Dimitrios Markatos
In Part 1 we looked at how to add an element to the cache, retrieve
an element, and remove an element. In this part we'll look at the Cache.Insert
method, which
allows for more powerful semantics with inserting an item into the data cache.
Inserting an Item into the Cache
As we saw earlier, inserting an item into the data cache is as simple as saying
Cache("key") = value
.
Items can also be inserted into the cache using the Insert
method, which allows for
more powerful semantics on how the item in the cache should be handled. Specifically, the Insert
method has four overloaded forms, as shown below:
Insert(key as String, value as Object)
- Inserts the objectvalue
into the cache, giving the item the key namekey
. This is semantically equivalent to usingCache("key") = value
.Insert(key as String, value as Object, dependencies as CacheDependency)
- Inserts the objectvalue
into the cache with key namekey
and dependencies specified by thedependencies
parameter. We'll discuss cache dependencies shortly.Insert(key as String, value as Object, dependencies as CacheDependency, absoluteExpiration as DateTime, slidingExpiration as TimeSpan)
- Inserts the objectvalue
into the cache with key namekey
, dependenciesdependencies
, and (time-based) expiration policies. Expiration policies, as we'll discuss soon, specify when the item should be evicted from the cache.Insert(key as String, value as Object, dependencies as CacheDependency, absoluteExpiration as DateTime, slidingExpiration as TimeSpan, priority as CacheItemPriority, onRemoveCallBack as CacheItemRemovedCallback)
- Inserts the objectvalue
into the cache with key namekey
, dependenciesdependencies
, (time-based) expiration policies, a cache priority, and a callback delegate. The priority specifies how important it is for the cache item to remain in the cache. That is, items with a lower priority will be evicted from the cache before items with a higher priority. The callback delegate provides a means for you to create your own function that is automatically called when the item is evicted from the cache.
The above list of the various forms of the Insert
method may look quite daunting.
Most often you'll likely use either form 1, 2, or 3. Let's take a moment to discuss the CacheDependency
and absolute and sliding time parameters.
Recall that information stored in the data cache is being stored on the Web server's memory. In a perfect
world, when an item is added to the cache using Insert(key, value)
or
Cache("key") = value
, the item will remain in the cache forever. Unfortunately this is
not plausible in the real world. If the computer the Web server runs on is rebooted, or shuts off,
for example, the cache will be lost. Even if the Web server machine is running, you may lose items from
the cache.
To see why, imagine that your Web server has allocated one MB of memory for storing items in the data cache. Now, imaging that you've added a number of items to the data cache such that you've used exactly 1 MB of memory. Great. Now, what happens when you add another item to the data cache? In order for the new item to "fit," the data cache needs to make room for it by removing an existing item. The existing item that is chosen to be removed is said to be evicted.
There may be times when you don't want an item to exist in the cache indefinitely. For example, say that you were displaying an XML file in an ASP.NET DataGrid. (See XML, the DataSet, and a DataGrid for an article illustrating how to accomplish this!) Rather than load the XML file into a DataSet and bind the DataSet to the DataGrid each page view, you may opt to cache the DataSet in the data cache. This option would work great until the XML file was altered; at that point, if you were still displaying the cached DataSet the user would be seeing stale information.
To overcome this problem, you can add the DataSet to the data cache, but specify that the XML file it represents is a cache dependency. By setting this file as a cache dependency, when the file changes the DataSet will be automatically evicted from the cache. That means the next time the DataSet is attempted to be read from the cache, it will not be found (since it has been evicted) and will be recreated by repopulating the DataSet from the XML file. This is desired since the XML file has changed since the DataSet was last cached. In order to insert an item with a cache dependency, you can do:
Cache.Insert("key", myDataSet, New CacheDependency(Server.MapPath("data.xml")))
|
If you wish to have the cache item evicted from the cache in an absolute time, say, five minutes from
when it was inserted into the cache, you can use the third form of the Insert
method, whose
fourth parameter expects a DateTime
value specifying the absolute time. The following
code illustrates how to add an item to the cache that will expire five minutes from when it was added
and has no cache dependencies:
Cache.Insert("key", value, Nothing, DateTime.Now.AddMinutes(5), TimeSpan.Zero)
|
In C# you would use null
instead of Nothing
to signify that you do not
want a cache dependency.
Note that since we do not want to specify a sliding time expiration, we set the last parameter to
TimeSpan.Zero
. Whereas an absolute time specifies that the item should be evicted from
the cache at a specific time, the sliding time eviction parameter specifies that the cache item
should be evicted if it is not referenced in a certain timespan. That is, if we set the timespan parameter
to, say, TimeSpan.FromSeconds(30)
, the cache item will be evicted if it is not referenced within
30 seconds. If it is referenced within 30 seconds, it will be evicted if it's not referenced in
another 30 seconds from when it was last referenced, and so on. An example of this would be:
Cache.Insert("key", value, Nothing, DateTime.Now, TimeSpan.FromSeconds(30), TimeSpan.Zero)
|
Note that when using the sliding time expiration parameter, the absolute expiration parameter value
does not matter. That is, it is automatically set to DateTime.Now
and has the
sliding time added to it to determine the absolute time the cache item should expire. Of course, if
the item is referenced within that time period, the calculation is redone and the absolute expiration time
is reset.
Before we move on to some examples, let's take a quick look at the CacheItemRemovedCallback
delegate. Recall that you can set this in the fourth overloaded form of the Insert
method.
The CacheItemRemovedCallback
specifies a function that is called when the item has been
evicted from the cache. To use the CacheItemRemovedCallback
you need to first create
a function that has the definition:
Sub CallbackFunction(String, Object, CacheItemRemovedReason)
|
The CacheItemRemovedReason
is an enumeration that explains why the item was removed from
the cache. Its entries include:
DependencyChanged
- the item was removed because its cache dependency was changed.Expired
- the item was removed because it expired (either by absolute or sliding time expiration).Removed
- the item was explicitly removed via theRemove
method.Underused
- the item was evicted by the cache because the system needed to free up memory.
To add a CacheItemRemovedCallback
function to an added cache item you will need to create
the appropriate function and a delegate variable that is wired up to the function, as shown below:
|
I am not going to go into detail discussing the CacheItemPriority
option, since I've
found very few real-world uses for this property (in designing Web applications). To learn more about
this enumeration and how to use it with the Insert
see the
technical
documentation.
At this point we've discussed the basics of data caching and the specifics of the Cache.Insert
method. In Part 3 we'll examine a real-world caching example:
a pagable DataGrid that uses a cached DataSet to save on database hits.