Recipe 16.8. Caching Application Data


Problem

Your application draws on data that is expensive to create from a performance perspective, so you want to store it in memory to be accessed by users throughout the lifetime of the application. The problem is that the data changes occasionally and you need to refresh the data after it changes.

Solution

Place the data in the Cache object with a dependency set to the source of the data so the data will be reloaded when it changes. Examples 16-5 and 16-6 show the code we've written to demonstrate this solution. In this case, we have created a class, CH16CacheService, with methods that place some sample XML book data in the Cache object. In our example, the book data is automatically removed from the cache any time the XML file is changed.

Discussion

The Cache object in ASP.NET provides the ability to store application data in a manner similar to the storing of data in the Application object. The Cache object, unlike the Application object, lets you specify that the cached data is to be replaced at a specified time or whenever there is a change to the original source of the data.

Examples 16-5 (VB) and 16-6 (C#) show the CH16CacheService class with two methods we created for this example. The first method (getBookData) provides access to the data stored in the Cache object with the appropriate checking (to ensure the data is still valid) and reloading as required. The getBookData method performs the following operations:

  1. Gets a reference to the book data in the Cache object

  2. Checks the reference to ensure the data is still valid and reloads the data using the loadBookDataInCache method if it is not

  3. Returns a reference to the book data

The second method (loadBookDataInCache) provides the ability to load the book data initially into the Cache. It performs the following operations:

  1. Reads the book data from the XML file into a DataSet

  2. Stores the DataTable in the DataSet in the Cache object

  3. Returns a reference to the book data

When the DataTable in the DataSet is stored in the Cache object, three parameters are passed to the Insert method of the Cache object. The first parameter is the "key" value used to access the data in the cache. A constant is used here since the key value is needed in several places in the code. The second parameter is the DataTable containing the book data. The third parameter is the dependency on the XML file that was the original source of the data. By adding this dependency, the data is automatically removed from the cache any time the XML file is changed.

 

context.Cache.Insert(CAC_BOOK_DATA, _ ds.Tables(BOOK_TABLE), _ New CacheDependency(xmlFilename))

context.Cache.Insert(CAC_BOOK_DATA, ds.Tables[BOOK_TABLE], new CacheDependency(xmlFilename));

A DataTable is being stored in the Cache object instead of a DataSet because a DataTable uses less system resources and the extra functionality of a DataSet is not needed.


The getBookData method provides access to the data stored in the Cache object with the appropriate checking to ensure the data is still valid and reloading it as required.

The first step to retrieving the cached data is to get a reference to the book data in the Cache object:

 

bookData = CType(context.Cache.Item(CAC_BOOK_DATA), _ DataTable)

bookData = (DataTable)(context.Cache[CAC_BOOK_DATA]);

Next, the reference must be checked to ensure the data is still valid and, if it is not, the data must be reloaded using the loadBookDataInCache method:

 

If (IsNothing(bookData)) Then 'data is not in the cache so load it bookData = loadBookDataInCache(context) End If

if (bookData == null) { // data is not in the cache so load it bookData = loadBookDataInCache(context); }

The caching object provides many additional features not described in our example, including the ability to replace the data based on a specified time and the ability to have one object in the cache be dependent on another object in the cache. For more information on these topics, refer to the MSDN documentation on the Cache and CacheDependency objects. For an example of caching application data dependent on data in a database, see Recipe 16.8.

Avoiding Race Conditions

The code shown in our example is designed to avoid a race condition that can result in a difficult-to-find error. The race condition is best described by example. Assume the following code was used (VB code shown):

 1 If (IsNothing(context.Cache.Item(CAC_BOOK_DATA)) then 2    loadBookDataInCache(context) 3  End If 4  bookData = Ctype(context.Cache.Item(CAC_BOOK_DATA), _ 5                   DataTable) 

The code shown on line 1 checks to see if the book data exists in the cache, but it does not retrieve the data. If the data is valid, the next line of code to execute will be line 4. If the dependency causes the data to be removed from the cache between the execution of lines 1 and 4, a null reference exception will be thrown at line 4 because the data is no longer in the cache.

This example precludes the problem by retrieving the data as the first step and then checking to see if the data is valid. Because you have a copy of the data, you do not care if the data is removed from the cache. Likewise, the loadBookDataInCache method returns the data to avoid the same race condition problem.


See Also

Recipe 16.8 and MSDN documentation on the Cache and CacheDependency

Example 16-5. Using application data in cache (.vb)

 Option Explicit On  Option Strict On Imports System.Data  Imports System.Data.SqlClient Namespace ASPNetCookbook.VBExamples    ''' <summary>    ''' This class provides caching functions used in chapter 16    ''' </summary>    Public Class CH16CacheService     'the following constant used to define the name of the variable used to 'store the book data in the cache object Private Const CAC_BOOK_DATA As String = "BookData" '''*********************************************************************** ''' <summary> ''' This routine reads the book data from an XML file and places it in ''' the cache object. ''' </summary> ''' ''' <param name="context">Set to the current Http context</param> ''' ''' <returns>DataTable containing book data loaded into the cache</returns> Private Shared Function loadBookDataInCache(ByVal context As HttpContext) _                    As DataTable       Const BOOK_TABLE As String = "Book"   Dim xmlFilename As String   Dim ds As DataSet   'read book data from XML file   xmlFilename = context.Server.MapPath("xml") & "\books.xml"   ds = New DataSet   ds.ReadXml(xmlFilename)   'store datatable with book data in cache scope with a   'dependency to the original XML file   context.Cache.Insert(CAC_BOOK_DATA, _                            ds.Tables(BOOK_TABLE), _     New CacheDependency(xmlFilename))       'return the data added to the cache   Return (ds.Tables(BOOK_TABLE))     End Function 'loadBookDataInCache '''*********************************************************************** ''' <summary> ''' This routine gets the book data from cache and reloads the cache if ''' required. ''' </summary> ''' ''' <param name="context">Set to the current Http context</param> ''' ''' <returns>DataTable containing book data from the cache</returns> Public Shared Function getBookData(ByVal context As HttpContext) _            As DataTable       Dim bookData As DataTable   'get the book data from the cache   bookData = CType(context.Cache.Item(CAC_BOOK_DATA), _                        DataTable)       'make sure the data is valid   If (IsNothing(bookData)) Then         'data is not in the cache so load it bookData = loadBookDataInCache(context)       End If   Return (bookData)      End Function 'getBookData    End Class 'CH16CacheService  End Namespace 

Example 16-6. Using application data in cache (.cs)

 using System; using System.Data; using System.Data.SqlClient;  using System.Web; using System.Web.Caching; namespace ASPNetCookbook.CSExamples {    /// <summary>    /// This class provides caching functions used in chapter 16    /// </summary>    public class CH16CacheService    {     // the following constant used to define the name of the variable used to // store the book data in the cache object private const String CAC_BOOK_DATA = "BookData"; '''*********************************************************************** /// <summary> /// This routine reads the book data from an XML file and places it in /// the cache object. /// </summary> /// /// <param name="context">Set to the current Http context</param> /// /// <returns>DataTable containing book data loaded into the cache</returns> private static DataTable loadBookDataInCache(HttpContext context) {       const String BOOK_TABLE = "Book";   String xmlFilename = null;   DataSet ds = null;   // read book data from XML file   xmlFilename = context.Server.MapPath("xml") + "\\books.xml";   ds = new DataSet();   ds.ReadXml(xmlFilename);   // store datatable with book data in cache scope with a   // dependency to the original XML file   context.Cache.Insert(CAC_BOOK_DATA,                            ds.Tables[BOOK_TABLE],     new CacheDependency(xmlFilename));       // return the data added to the cache   return (ds.Tables[BOOK_TABLE]);    } // loadBookDataInCache    '''***********************************************************************    /// <summary>    /// This routine gets the book data from cache and reloads the cache if    /// required.    /// </summary>    ///    /// <param name="context">Set to the current Http context</param>    ///    /// <returns>DataTable containing book data from the cache</returns>    public static DataTable getBookData(HttpContext context)    {      DataTable bookData = null;  // get the book data from the cache  bookData = (DataTable)(context.Cache[CAC_BOOK_DATA]);  // make sure the data is valid  if (bookData == null)  {        // data is not in the cache so load it    bookData = loadBookDataInCache(context);      }  return (bookData);   } // getBookData   } // CH16CacheService  } 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2003
Pages: 202

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