Application Data Caching Using the Cache API


The Cache API provides a mechanism for caching individual objects, as opposed to Web pages. The Cache object is instanced once per application, and it remains in memory until the application gets restarted. The Cache object is basically a dictionary object that supports specialized Add() , Insert() , and Remove() methods for updating items in the cache.

The Cache object allows for very granular control over cached content because it provides a flexible set of methods for updating objects in the cache. In addition, the Cache object is optimized to persist cache items only for as long as they are needed. The Cache object provides the following useful features:

  • Simple interface: You can store cache items using a straightforward key-value pair assignment. This makes it easy to add, retrieve, and remove items from the cache. In fact, you must reference cache items using their key name , rather than using an index value. This reflects that items may be shuffled around within the cache for efficiency purposes, or even removed, so indexes have less meaning than actual key names . By comparison, you can reference items stored in Session and Application objects either by name or by index.

  • Customized expiration: You can assign cache items individual expiration policies. The Cache class supports three kinds of expiration: absolute, sliding, and dependency-based expiration.

  • Memory management: The Cache class supports a mechanism called scavenging , which optimizes memory usage when the available server memory becomes limited. This mechanism automatically removes unused, or infrequently used, items from the cache. The Cache class automatically assigns priority levels to items to determine the order in which items will be scavenged from memory. Underused items will be evicted from the cache before more popular items. In addition, you may override the priority level on any item in the cache.

  • Concurrency management: The Cache class automatically handles updates from concurrent users. Unlike the Application object, you do not need to explicitly lock the Cache object when performing updates on its items. This does not eliminate the confusion caused when multiple users simultaneously update the same item. However, it does prevent locking problems.

  • Validation callbacks: The Cache class supports callbacks to delegate functions when items are removed from the cache. This is useful for taking action when an item is (unexpectedly) removed from the cache.

It is inevitable to compare the Cache object against its more familiar counterparts, the Session and Application objects. Each of these three objects has their advantages in certain situations. However, overall, the Cache object provides the flexibility and ease of use that makes it a good choice in a wide variety of situations. (Chapter 4, "Optimizing Application and Session State Management," discusses the Session and Application objects).

Understanding the Cache API

The Cache class is a member of the System.Web.Caching namespace, and it is instanced once per application instance. You can access the Cache object directly via the Page object's Cache property. You can also access it via the Cache property of both the HttpRuntime and HttpContext classes. The Cache class is a dictionary that provides specialized Add() , Insert() , and Remove() methods for updating items. It also supports an IEnumerable interface, which makes it easy to loop through items in the cache. ASP.NET handles the expiration and scavenging mechanisms that help optimize the Cache object's memory usage. Table 5-4 describes important members of the Cache class.

Table 5-4: Cache Class Members

MEMBER

DESCRIPTION

Add()

This method adds an item to the cache with the following parameters:

key name [string] and value [object]

dependencies : A CacheDependency object, which specifies a resource that the cache is dependent on. You can set this to "Nothing" if no dependencies exist.

absoluteExpiration : A Date object, which specifies when the item expires and is removed from the cache. Set to the field value: NoAbsoluteExpiration , if no expiration policy is required. May not be specified in conjunction with a sliding expiration policy.

slidingExpiration : A TimeSpan object, which specifies the interval over which the item must go unused before it expires and is removed from the cache. Must be no greater than one year from the current date-timestamp. Set to the field value: NoSlidingExpiration , if no expiration policy is required. May not be specified in conjunction with an absolute expiration policy.

priority : A CacheItemPriority enumeration value, which sets the item's relative priority in the cache. Objects are assigned a Normal priority by default. Higher priority objects are less likely to be evicted when the cache runs low on resources and starts to scavenge items. Expensive objects (in other words, time-consuming and processor- intensive to create) should be assigned a higher relative priority compared to cheaper objects.

onRemoveCallback : A delegate function that is called when an item is removed from the cache. This serves to notify the application that the item is no longer available in the cache. You can set this to "Nothing" if no delegate is required.

The Add() method is not overloaded, so you must set every parameter. The Add() method returns a reference to the item that is being added to the cache.

Insert()

This method inserts an item to the cache using the same parameters as the Add method (see above). This method is overloaded as follows :

 key, value key, value, dependencies key, value, dependencies, absoluteExpiration, slidingExpiration key, value, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback 

Unlike the Add() method, the Insert() method does not return a reference to the item that is being added to the cache.

Get()

This method retrieves an item from the cache using the key name.

Remove()

This method removes an item from the cache using the key name. The method returns a reference to the item that is being removed from the cache.

GetEnumerator()

This method retrieves an IDictionaryEnumerator object that you can use to iterate through the items in the cache.

NoAbsoluteExpiration

This read-only field may be used in place of an absolute expiration value. The field value is equivalent to the largest possible expiration date: 12/31/9999 11:59:59 PM.

NoSlidingExpiration

This read-only field may be used in place of a sliding expiration value. The field value is equivalent to zero, which means that the absolute expiration policy will be in effect instead.

Understanding Cache Expiration Policies

Before discussing how to add items to the cache, we need to explore the three available Cache expiration policies:

  • Absolute expiration: This is an absolute date and time when an item expires and is removed from the cache. Absolute expiration is best suited for items that have a well-defined useful lifespan, such as time-sensitive reference information.

  • Sliding expiration: This is an interval over which the item must go unused before it is expired and removed from the cache. For example, if the interval is set to 30 minutes, then the item will expire if it has not been accessed during the last 30 minutes. Sliding expiration is best suited for items that experience heavy usage initially, followed by declining usage over time.

  • Dependency expiration: This cached item may be linked to a resource and expired only once this resource changes. Dependency resources may include files, directories, or other items in the cache. For example, an item may link to an XML file. The item remains in the cache until the XML file changes. Dependency expiration is best suited for items that change infrequently.

ASP.NET offers an impressive range of cache expiration policies, which are easy to use and offer few constraints. You cannot set absolute expiration and sliding expiration policies together. You will get an error if you attempt to define them both for the same item. You can set dependency expiration in conjunction with either absolute or sliding expiration policies; however, this defeats the purpose of using it. Dependency expiration is an excellent alternative to using the Application object. This policy automatically updates the cached item when its dependency changes and does not require that the application be restarted.

The Cache object allows you to specify a different expiration policy for every cached item. You can even assign a relative priority to a cached item to influence the sequence in which it gets scavenged. Scavenging is simply the forced expiration of cached items for the purpose of optimizing memory usage. By default, the cache will evict items from the cache based on how frequently they are accessed, so you are not required to set specific priority levels. If server resources become limited, then all Cache items face the possibility of being scavenged and expired from the cache before their assigned expiration time. The best way to ensure your cached item will remain in memory is to specify an expiration policy and to assign it a high priority.

Adding Cache Items

You add items to the cache in three ways:

  • Key-value assignment: This is the implicit assignment of a key-value pair, in the same way that you would assign an item to a Session object. The value stores an object data type, which means you can assign any .NET data type to the cache.

  • Add() method: This method adds an item to the cache and specifies its expiration policy, its priority, and a delegate function that notifies the application when the item is removed from the cache. This method returns a reference to the object that is being added to the cache.

  • Insert() method: This method adds an item to the cache but does not return an object reference to the item. Unlike the Add() method, the Insert() method is overloaded and lets you specify four different combinations of parameters.

Table 5-4 summarizes the Add() and Insert() methods in detail, so this section focuses on code examples.

The simplest way to add an item to the cache is to implicitly assign the value to a key name, in the same way that you would assign a Session variable:

 Dim sqlDS As DataSet Page.Cache("MyDS") = sqlDS 

The disadvantage with this approach is that you cannot specify the expiration policy for the object. This is how you define an absolute expiration using the explicit Add() method:

 Dim sqlDS As DataSet Page.Cache.Add("MyDS", sqlDS, Nothing, DateTime.Now.AddMinutes(20), _     Cache.NoSlidingExpiration, CacheItemPriority.Normal, Nothing) 

Recall that the Add() method is not overloaded, which means every parameter must be specified, even if you do not require all of them. Alternatively, you can use the Insert() method, which is overloaded and therefore more flexible. This is how you insert an item into the cache with a 20-minute sliding expiration:

 Dim sqlDS As DataSet Me.Cache.Insert("MyDS", sqlDS, Nothing, Me.Cache.NoAbsoluteExpiration, _     New TimeSpan(0, 0, 20)) 

Dependency expiration is almost as easy to implement. Consider Listing 5-9, which shows a Web form that contains a DataGrid control and a Submit button. When the page first loads, a DataSet gets generated from a sales query in the Northwind database. The DataSet contents also get written out to an XML file called SalesQuery.xml . Next , the DataSet gets added to the cache using the XML file as its dependency resource. The DataSet will remain in the cache as long as this XML file remains unchanged. On subsequent postbacks, the DataSet gets retrieved from the cache. If the dependency file changes, then the DataSet will be regenerated and again added to the cache. Listing 5-9 shows the code for caching with dependency expiration.

Listing 5-9: Caching with Dependency Expiration
start example
 Imports System.Data Imports System.Web.Caching Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid Private Sub Page_Load(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles MyBase.Load     Dim sqlDS As DataSet     Dim objDependency As CacheDependency     If Not Page.IsPostBack Then         ' Generate a DataSet, and write it out to XML         sqlDS = GenerateDataSet()         sqlDS.WriteXml("C:\MyCache\SalesQuery.xml")         ' Insert the DataSet into the Cache, using dependency-based expiration         objDependency = New CacheDependency("C:\MyCache\SalesQuery.xml")         Me.Cache.Insert("MyDS", sqlDS, objDependency)     Else         ' Retrieve the DataSet from Cache, if it is available         If IsNothing(sqlDS) Then             ' Regenerate the DataSet             sqlDS = GenerateDataSet()         Else             sqlDS = Me.Cache.Get("MyDS")         End If     End If     BindDataGrid(sqlDS) ' Bind the DataSet to the DataGrid     sqlDS = Nothing End Sub 
end example
 

Listing 5-9 uses two helper functions: GenerateDataSet() to retrieve the DataSet and BindDataGrid() to bind the DataSet to the data grid. You can find the code for these functions in the sample project files.

Listing 5-10 shows the SalesQuery.xml file.

Listing 5-10: SalesQuery.xml
start example
 <?xml version="1.0" standalone=" yes"?> <NewDataSet>   <Table>     <Country>UK</Country>     <LastName>Suyama</LastName>     <FirstName>Michael</FirstName>     <ShippedDate>1996-07-10T00:00:00.0000000-07:00</ShippedDate>     <OrderID>10249</OrderID>     <SaleAmount>1863.4</SaleAmount>   </Table> </NewDataSet> 
end example
 

You can test dependency caching by altering a value in the SalesQuery.xml file and then stepping through the postback code. For example, alter the < OrderID > value from 10249 to 10248. Repost the page, and you will observe that the compiler bypasses the cached DataSet and regenerates a fresh one.

Finally, you may need to sometimes force a cache item to update. You can add or insert the same key multiple times, and usually the item will update without errors. However, it is safer to first remove the item and then add it to the cache again.

Retrieving Cache Items

You can retrieve cache items individually by their key name using the Get() method. Recall that cached items are not guaranteed to remain in the cache, so you should always test for an item's existence before you attempt to assign it to a reference variable:

 If Not IsNothing(Page.Cache.Item("MyDS")) Then      sqlDS = Page.Cache.Get("MyDS") Else      sqlDS = GenerateDataSet() ' Regenerate the DataSet End If 

The Cache object supports an enumeration interface that allows you to iterate through all of the items in the cache:

 Dim enmKeys As IDictionaryEnumerator = Me.Cache.GetEnumerator While enmKeys.MoveNext     If enmKeys.Value.GetType.ToString = "System.String" Then         Response.Write(enmKeys.Key & "= "& enmKeys.Value & "<BR>")     End If End While 

Recall that the value property actually holds an object reference, which may not cast to a string or to any renderable data type. The Response.Write() method will error out for such items, so you should always check the data type of the object before you attempt to write out its value.

Removing Cache Items

You can remove cache items individually by their key name using the Remove() method. This method returns a reference to the item being removed. If the item is not found, then the method will return "Nothing":

 Dim MyDS As DataSet MyDS  = Page.Cache.Remove("MyDS") ' Evaluates to True MyDS  = Page.Cache.Remove("MyDS2") ' Method returns Nothing 

Cache items will also be removed automatically based on their expiration policies.

Callback Notification

We have pointed out several times that cache items are not guaranteed to remain in the cache. An item may be evicted from the cache if server resources become limited or if an item is being underused. The Cache object supports a callback mechanism that enables it to notify the application if an item gets removed. The callback is handled by one or more delegate functions that you can specify when an item gets added to the cache. The callback mechanism is especially useful in situations where the application needs to monitor items that are removed unexpectedly.

Listing 5-11 shows you how to set up callback notification. This listing is a variation of Listing 5-10. The callback function is called ItemRemovedCallback() , which channels the key name, the value, and a variable of type CacheItemRemovedReason . This type is an enumeration that indicates the reason the item was removed from the cache. The listing generates a DataSet and assigns it to the cache with an absolute expiration of five seconds. When the user posts the page back, the listing attempts to retrieve the cached item. Assuming that five or more seconds have passed, the compiler will immediately jump to the ItemRemovedCallback() function. Once this function executes, the compiler returns to the next line in the postback section, and the code proceeds uninterrupted.

Listing 5-11: Setting Up Callback Notification
start example
 Private Sub Page_Load(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles MyBase.Load     If Not Page.IsPostBack Then         Dim sqlDS As DataSet         sqlDS = GenerateDataSet() ' Generate the DataSet         ' Insert object to cache with absolute expiration and a callback function         Dim MyCallbackFunction As System.Web.Caching.CacheItemRemovedCallback         MyCallbackFunction = New CacheItemRemovedCallback( _             AddressOf Me.ItemRemovedCallback)         Me.Cache.Add("MyDS", sqlDS, Nothing, DateTime.Now.AddSeconds(5), _             Cache.NoSlidingExpiration, CacheItemPriority.Low, MyCallbackFunction)    Else         ' Retrieve the DataSet from Cache, if it is available         sqlDS = Me.Cache.Get("MyDS") ' Delegates to callback function if applicable         If IsNothing(sqlDS) Then             sqlDS = GenerateDataSet() ' Regenerate the DataSet         End If         BindDataGrid(sqlDS) ' Bind the DataSet to the DataGrid     End If End Sub Public Sub ItemRemovedCallback(ByVal strKey As String, ByVal objValue As Object, _     ByVal r As CacheItemRemovedReason)     Dim strJSScript As String     Dim strReason As String = "The item ["& strKey & "] expired."     If r <> CacheItemRemovedReason.Expired Then _         strReason = "The item was underused or evicted."     Response.Write(strReason) End Sub 
end example
 

By definition, cache items can be re-created, so your application should never encounter an unrecoverable error in the event that an item is unexpectedly evicted from the cache. Still, the callback mechanism is useful when the timing of an eviction has the potential to cause problems for the application.




Performance Tuning and Optimizing ASP. NET Applications
Performance Tuning and Optimizing ASP.NET Applications
ISBN: 1590590724
EAN: 2147483647
Year: 2005
Pages: 91

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