|  13.6.1 Problem  Your application draws on data that is expensive to create from a performance perspective, so you want to store it in memory, where it can 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 when it changes.   13.6.2 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. Example 13-3 and Example 13-4 show the code we've written to demonstrate this solution. In this case, these are VB and C# code-behind files for  Global.asax  that place some sample XML book data in the  Cache  object. In our example, the book data is automatically removed from the cache anytime the XML file is changed.   13.6.3 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.   In Example 13-3 and Example 13-4, two methods have been added to  global.asax.vb  (or  global.asax.cs  for C#). 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 initially load the book data 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 anytime the XML file is changed.     |    |   |  A  DataTable  is being stored in the  Cache  object instead of a  DataSet  because it uses less system resources and the extra functionality of a  DataSet  is not needed.  |  |  
    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));  
  The  getBookData  method is added to  global.asax.vb  (or  global.asax.cs  for C#) to provide 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); } 
  Finally, the book data is returned to the caller.     |  Avoiding Race Conditions  The code shown in our example is designed to avoid a race condition that can result in a very 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), _ 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 was valid, the next line of code that would execute would be line 4. If the dependency caused the data to be removed from the cache between the execution of lines 1 and 4, a null reference exception would 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 then checks to see if the data is valid. Because you already 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.  |  
  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.   One dependency that is not provided is the ability to replace the data when data in a database changes. A workaround is to write the data to an XML document when the application starts, and then use the approach shown in this example. When an operation changes the data in the database, the XML document can be regenerated, which will cause the data to be removed from the cache and then reloaded when it is needed the next time.   13.6.4 See Also  MSDN documentation on the  Cache  and  CacheDependency  objects   Example 13-3. Using application data in cache (.vb)  Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: Global.asax.vb ' ' Description: This module provides the code behind for the ' Global.asax page ' '***************************************************************************** Imports Microsoft.VisualBasic Imports System Imports System.Configuration Imports System.Data Imports System.Data.OleDb Imports System.Diagnostics Imports System.Web Imports System.Web.Caching Namespace ASPNetCookbook.VBExamples Public Class Global Inherits System.Web.HttpApplication '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" '************************************************************************* ' ' ROUTINE: loadBookDataInCache ' ' DESCRIPTION: This routine reads the book data from an XML file and ' places it in the cache object. '-------------------------------------------------------------------------  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  '************************************************************************* ' ' ROUTINE: loadBookDataInCache ' ' DESCRIPTION: This routine gets the book data from cache and reloads ' the cache if required. '-------------------------------------------------------------------------  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 'Global End Namespace 
  Example 13-4. Using application data in cache (.cs)  //---------------------------------------------------------------------------- // // Module Name: Global.asax.cs // // Description: This module provides the code behind for the // Global.asax page // //**************************************************************************** using System; using System.Configuration; using System.Data; using System.Data.OleDb; using System.Diagnostics; using System.Web; using System.Web.Caching; namespace ASPNetCookbook.CSExamples { public class Global : System.Web.HttpApplication { // 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"; //************************************************************************ // // ROUTINE: loadBookDataInCache // // DESCRIPTION: This routine reads the book data from an XML file and // places it in the cache object. //------------------------------------------------------------------------  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  //************************************************************************ // // ROUTINE: getBookData // // DESCRIPTION: This routine gets the book data from cache and reloads // the cache if required. //------------------------------------------------------------------------  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  } // Global } 
 |