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: Gets a reference to the book data in the Cache object Checks the reference to ensure the data is still valid and reloads the data using the loadBookDataInCache method if it is not 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: Reads the book data from the XML file into a DataSet Stores the DataTable in the DataSet in the Cache object 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 } | |