WEBSITE

Microsoft ASP.NET 3.5 : Caching Application Data (part 2) - Working with the ASP.NET Cache

1/12/2013 3:19:43 AM

2. Working with the ASP.NET Cache

An instance of the Cache object is associated with each running application and shares the associated application’s lifetime. The cache holds references to data and proactively verifies validity and expiration. When the system runs short of memory, the Cache object automatically removes some little-used items and frees valuable server resources. Each item when stored in the cache can be given special attributes that determine a priority and an expiration policy. All these are system-provided tools to help programmers control the scavenging mechanism of the ASP.NET cache.

Inserting New Items in the Cache

A cache item is characterized by a handful of attributes that can be specified as input arguments of both Add and Insert. In particular, an item stored in the ASP.NET Cache object can have the following properties:

  • Key A case-sensitive string, it is the key used to store the item in the internal hashtable the ASP.NET cache relies upon. If this value is null, an exception is thrown. If the key already exists, what happens depends on the particular method you’re using: Add fails, while Insert just overwrites the existing item.

  • Value A non-null value of type Object that references the information stored in the cache. The value is managed and returned as an Object and needs casting to become useful in the application context.

  • Dependencies Object of type CacheDependency, tracks a physical dependency between the item being added to the cache and files, directories, database tables, or other objects in the application’s cache. Whenever any of the monitored sources are modified, the newly added item is marked obsolete and automatically removed.

  • Absolute Expiration Date A DateTime object that represents the absolute expiration date for the item being added. When this time arrives, the object is automatically removed from the cache. Items not subject to absolute expiration dates must use the NoAbsoluteExpiration constants representing the farthest allowable date. The absolute expiration date doesn’t change after the item is used in either reading or writing.

  • Sliding Expiration A TimeSpan object, represents a relative expiration period for the item being added. When you set the parameter to a non-null value, the expiration-date parameter is automatically set to the current time plus the sliding period. If you explicitly set the sliding expiration, you cannot set the absolute expiration date too. From the user’s perspective, these are mutually exclusive parameters. If the item is accessed before its natural expiration time, the sliding period is automatically renewed.

  • Priority A value picked out of the CacheItemPriority enumeration, denotes the priority of the item. It is a value ranging from Low to NotRemovable. The default level of priority is Normal. The priority level determines the importance of the item; items with a lower priority are removed first.

  • Removal Callback If specified, indicates the function that the ASP.NET Cache object calls back when the item will be removed from the cache. In this way, applications can be notified when their own items are removed from the cache no matter what the reason is. When the session state works in InProc mode, a removal callback function is used to fire the Session_End event. The delegate type used for this callback is CacheItemRemoveCallback.

There are basically three ways to add new items to the ASP.NET Cache object—the set accessor of the Item property, the Add method, and the Insert method. The Item property allows you to indicate only the key and the value. The Add method has only one signature that includes all the aforementioned arguments. The Insert method is the most flexible of all options and provides the following four overloads:

public void Insert(string, object);
public void Insert(string, object, CacheDependency);
public void Insert(string, object, CacheDependency, DateTime, TimeSpan);
public void Insert(string, object, CacheDependency, DateTime, TimeSpan,
    CacheItemPriority, CacheItemRemovedCallback);

The following code snippet shows the typical call that is performed under the hood when the Item set accessor is used:

Insert(key, value, null, Cache.NoAbsoluteExpiration,
    Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);

If you use the Add method to insert an item whose key matches that of an existing item, no exception is raised, nothing happens, and the method returns null.

Removing Items from the Cache

All items marked with an expiration policy, or a dependency, are automatically removed from the cache when something happens in the system to invalidate them. To programmatically remove an item, on the other hand, you resort to the Remove method. Note that this method removes any item, including those marked with the highest level of priority (NotRemovable). The following code snippet shows how to call the Remove method:

object oldValue = Cache.Remove("MyItem");

Normally, the method returns the value just removed from the cache. However, if the specified key is not found, the method fails and null is returned, but no exception is ever raised.

When items with an associated callback function are removed from the cache, a value from the CacheItemRemovedReason enumeration is passed on to the function to justify the operation. The enumeration includes the values listed in Table 3.

Table 3. The CacheItemRemovedReason Enumeration
ReasonDescription
DependencyChangedRemoved because the associated dependency changed.
ExpiredRemoved because expired.
RemovedProgrammatically removed from the cache using Remove. Notice that a Removed event might also be fired if an existing item is replaced either through Insert or the Item property.
UnderusedRemoved by the system to free memory.

If the item being removed is associated with a callback, the function is executed immediately after having removed the item.

Tracking Item Dependencies

Items added to the cache through the Add or Insert method can be linked to an array of files and directories as well as to an array of existing cache items, database tables, or external events. The link between the new item and its cache dependency is maintained using an instance of the CacheDependency class. The CacheDependency object can represent a single file or directory or an array of files and directories. In addition, it can also represent an array of cache keys—that is, keys of other items stored in the Cache—and other custom dependency objects to monitor—for example, database tables or external events.

The CacheDependency class has quite a long list of constructors that provide for the possibilities listed in Table 4.

Table 4. The CacheDependency Constructor List
ConstructorDescription
StringA file path—that is, a URL to a file or a directory name
String[]An array of file paths
String, DateTimeA file path monitored starting at the specified time
String[], DateTimeAn array of file paths monitored starting at the specified time
String[], String[]An array of file paths, and an array of cache keys
String[], String[], CacheDependencyAn array of file paths, an array of cache keys, and a separate CacheDependency object
String[], String[], DateTimeAn array of file paths and an array of cache keys monitored starting at the specified time
String[], String[], CacheDependency, DateTimeAn array of file paths, an array of cache keys, and a separate instance of the CacheDependency class monitored starting at the specified time

Any change in any of the monitored objects invalidates the current item. It’s interesting to note that you can set a time to start monitoring for changes. By default, monitoring begins right after the item is stored in the cache. A CacheDependency object can be made dependent on another instance of the same class. In this case, any change detected on the items controlled by the separate object results in a broken dependency and the subsequent invalidation of the present item.

Note

Starting with ASP.NET 2.0, cache dependencies underwent some significant changes and improvements in. In previous versions, the CacheDependency class was sealed and not further inheritable. As a result, the only dependency objects you could work with were those linking to files, directories, or other cached items. Now, the CacheDependency class is inheritable and can be used as a base to build custom dependencies. In addition, ASP.NET 2.0 and newer versions come with a built-in class to monitor database tables for changes. We’ll examine custom dependencies shortly.


In the following code snippet, the item is associated with the timestamp of a file. The net effect is that any change made to the file that affects the timestamp invalidates the item, which will then be removed from the cache.

CacheDependency dep = new CacheDependency(filename);
Cache.Insert(key, value, dep);

Bear in mind that the CacheDependency object needs to take file and directory names expressed through absolute file system paths.

Defining a Removal Callback

Item removal is an event independent from the application’s behavior and control. The difficulty with item removal is that because the application is oblivious to what has happened, it attempts to access the removed item later and gets only a null value back. To work around this issue, you can either check for the item’s existence before access is attempted or, if you think you need to know about removal in a timely manner, register a callback and reload the item if it’s invalidated. This approach makes particularly good sense if the cached item just represents the content of a tracked file or query.

The following code-behind class demonstrates how to read the contents of a Web server’s file and cache it with a key named “MyData.” The item is inserted with a removal callback. The callback simply re-reads and reloads the file if the removal reason is DependencyChanged.

void Load_Click(object sender, EventArgs e)
{
   AddFileContentsToCache("data.xml");
}
void Read_Click(object sender, EventArgs e)
{
   object data = Cache["MyData"];
   if (data == null)
   {
       contents.Text = "[No data available]";
       return;
   }
   contents.Text = (string) data;
}
void AddFileContentsToCache(string fileName)
{
   string file = Server.MapPath(fileName);
   StreamReader reader = new StreamReader(file);
   string buf = reader.ReadToEnd();
   reader.Close();

   // Create and display the contents
   CreateAndCacheItem(buf, file);
   contents.Text = Cache["MyData"].ToString();
}
void CreateAndCacheItem(object buf, string file)
{
   CacheItemRemovedCallback removal;
   removal = new CacheItemRemovedCallback(ReloadItemRemoved);

   CacheDependency dep = new CacheDependency(file);
   Cache.Insert("MyData", buf, dep, Cache.NoAbsoluteExpiration,
         Cache.NoSlidingExpiration, CacheItemPriority.Normal, removal);
}
void ReloadItemRemoved(string key, object value,
       CacheItemRemovedReason reason)
{
   if (reason == CacheItemRemovedReason.DependencyChanged)
   {
      // At this time the item has been removed. We get fresh data and
      // re-insert the item
      if (key == "MyData")
         AddFileContentsToCache("data.xml");

      // This code runs asynchronously with respect to the application,
      // as soon as the dependency gets broken. To test it, add some
      // code here to trace the event
   }
}
void Remove_Click(object sender, EventArgs e)
{
    Cache.Remove("MyData");
}


					  

Figure 2 shows a sample page to test the behavior of the caching API when dependencies are used. If the underlying file has changed, the dependency-changed event is notified and the new contents are automatically loaded. So the next time you read from the cache you get fresh data. If the cached item is removed, any successive attempt to read returns null.

Figure 2. A sample page to test the behavior of removal callbacks in the ASP.NET cache.

Note that the item removal callback is a piece of code defined by a user page but automatically run by the Cache object as soon as the removal event is fired. The code contained in the removal callback runs asynchronously with respect to the page. If the removal event is related to a broken dependency, the Cache object will execute the callback as soon as the notification is detected.

If you add an object to the Cache and make it dependent on a file, directory, or key that doesn’t exist, the item is regularly cached and marked with a dependency as usual. If the file, directory, or key is created later, the dependency is broken and the cached item is invalidated. In other words, if the dependency item doesn’t exist, it’s virtually created with a null timestamp or empty content.

Note

Once an item bound to one or more dependencies is removed from the cache, it stops monitoring for changes. Further changes to, say, the underlying file won’t be caught just because the item is no longer in the cache. You can verify this behavior by loading some data as in Figure 16-2. Next, you click Remove to dispose of the item and modify the underlying file. Later, if you try to re-read the item it’ll return null because the element is no longer in the cache.


To define a removal callback, you first declare a variable of type CacheItemRemovedCallback. Next, you instantiate this member with a new delegate object with the right signature.

CacheItemRemovedCallback removal;
removal = new CacheItemRemovedCallback(ReloadItemRemoved);

The CacheDependency object is simply passed the removal delegate member, which executes the actual function code for the Cache object to call back.

Tip

If you define a removal callback function through a static method, you avoid an instance of the class that contains the method to be kept in memory all the time to support the callback. Static methods (that is, Shared methods according to the Microsoft Visual Basic .NET jargon) are callable on a class even when no instance of the class has been created. Note, though, that this choice raises other issues as far as trying to use the callback to re-insert a removed item. In this case, therefore, you reasonably need to access a method on the page class, which is not permitted from within a static member. To work around this issue, you create a static field, say ThisPage, and set it to the page object (the this keyword in C# or Me in Visual Basic .NET) during the Page_Init event. You then invoke any object-specific method through the static ThisPage member, even from within a static method.


Setting the Item’s Priority

Each item in the cache is given a priority—that is, a value picked up from the CacheItemPriority enumeration. A priority is a value ranging from Low (lowest) to NotRemovable (highest), with the default set to Normal. The priority is supposed to determine the importance of the item for the Cache object. The higher the priority is, the more chances the item has to stay in memory even when the system resources are going dangerously down.

If you want to give a particular priority level to an item being added to the cache, you have to use either the Add or Insert method. The priority can be any value listed in Table 5.

Table 5. Priority Levels in the Cache Object
PriorityValueDescription
Low1Items with this level of priority are the first items to be deleted from the cache as the server frees system memory.
BelowNormal2Intermediate level of priority between Normal and Low.
Normal3Default priority level. It is assigned to all items added using the Item property.
Default3Same as Normal.
AboveNormal4Intermediate level of priority between Normal and High.
High5Items with this level of priority are the last items to be removed from the cache as the server frees memory.
NotRemovable6Items with this level of priority are never removed from the cache. Use this level with extreme care.

The Cache object is designed with two goals in mind. First, it has to be efficient and built for easy programmatic access to the global repository of application data. Second, it has to be smart enough to detect when the system is running low on memory resources and to clear elements to free memory. This trait clearly differentiates the Cache object from HttpApplicationState, which maintains its objects until the end of the application (unless the application itself frees those items). The technique used to eliminate low-priority and seldom-used objects is known as scavenging.

Controlling Data Expiration

Priority level and changed dependencies are two of the causes that could lead a cached item to be automatically garbage-collected from the Cache. Another possible cause for a premature removal from the Cache is infrequent use associated with an expiration policy. By default, all items added to the cache have no expiration date, neither absolute nor relative. If you add items by using either the Add or Insert method, you can choose between two mutually exclusive expiration policies: absolute and sliding expiration.

Absolute expiration is when a cached item is associated with a DateTime value and is removed from the cache as the specified time is reached. The DateTime.MaxValue field, and its more general alias NoAbsoluteExpiration, can be used to indicate the last date value supported by the .NET Framework and to subsequently indicate that the item will never expire.

Sliding expiration implements a sort of relative expiration policy. The idea is that the object expires after a certain interval. In this case, though, the interval is automatically renewed after each access to the item. Sliding expiration is rendered through a TimeSpan object—a type that in the .NET Framework represents an interval of time. The TimeSpan.Zero field represents the empty interval and is also the value associated with the NoSlidingExpiration static field on the Cache class. When you cache an item with a sliding expiration of 10 minutes, you use the following code:

Insert(key, value, null, Cache.NoAbsoluteExpiration,
    TimeSpan.FromMinutes(10), CacheItemPriority.Normal, null);

Internally, the item is cached with an absolute expiration date given by the current time plus the specified TimeSpan value. In light of this, the preceding code could have been rewritten as follows:

Insert(key, value, null, DateTime.Now.AddMinutes(10),
    Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);

However, a subtle difference still exists between the two code snippets. In the former case—that is, when sliding expiration is explicitly turned on—each access to the item resets the absolute expiration date to the time of the last access plus the time span. In the latter case, because sliding expiration is explicitly turned off, any access to the item doesn’t change the absolute expiration time.

Statistics About Memory Usage

Immediately after initialization, the Cache collects statistical information about the memory in the system and the current status of the system resources. Next, it registers a timer to invoke a callback function at one-second intervals. The callback function periodically updates and reviews the memory statistics and, if needed, activates the scavenging module. Memory statistics are collected using a bunch of Win32 API functions to obtain information about the system’s current usage of both physical and virtual memory.

The Cache object classifies the status of the system resources in terms of low and high pressure. Each value corresponds to a different percentage of occupied memory. Typically, low pressure is in the range of 15 percent to 40 percent, while high pressure is measured from 45 percent to 65 percent of memory occupation. When the memory pressure exceeds the guard level, seldom-used objects are the first to be removed according to their priority.

Other  
 
Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Exchange Server Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe Photoshop CorelDRAW X5 CorelDraw 10 windows Phone 7 windows Phone 8 Iphone