• home
  • forum
  • my
  • kt
  • download
  • Caching

    Author: 2007-07-04 15:47:21 From:

    After completing this tutorial, you will be able to
    • Improve the performance of your application by using the application cache
    • Avoid unnecessary round-trips to the database
    • Manage items in the cache

    This tutorial covers ASP.NET's built-in data caching features. Caching is a long-standing means of improving the performance of any software system. The idea is to place frequently used data in quickly accessed media. Even though access times for mass storage continue to improve, accessing data from a standard hard disk is much slower than accessing it in memory. By taking often-used data and making it available quickly, you can improve the performance of your application dramatically.

    After completing this tutorial, you will be able to

    • Improve the performance of your application by using the application cache

    • Avoid unnecessary round-trips to the database

    • Manage items in the cache

    This tutorial covers ASP.NET's built-in data caching features. Caching is a long-standing means of improving the performance of any software system. The idea is to place frequently used data in quickly accessed media. Even though access times for mass storage continue to improve, accessing data from a standard hard disk is much slower than accessing it in memory. By taking often-used data and making it available quickly, you can improve the performance of your application dramatically.

    The ASP.NET runtime includes a dictionary (key-value map) of CLR objects. The map (named Cache) lives with the application and is available via the HttpContext and System.Web.UI.Page. Using the cache is very much like using the Session object. You may access items in the cache using an indexer. In addition, you may control the lifetime of objects in the cache and even set up links between the cached objects and their physical data sources. Let's start by examining a case in which using the cache is justified.

    Making an Application that Benefits from Caching

    1. Create a new site. Call it UseDataCaching.

    2. Borrow the UseDataList code from the example in Tutorial 13. To bring it into your new project, right-click on the project in solution explorer. Choose Add Existing Item. Navigate the to the UseDataList.aspx and UseDataList.aspx.cs code from Tutorial 13.

      The code you imported refers to the database in the SessionState example. That's okay. If you want to, you can change it to the database in this application's App_Data directory, but it's not strictly necessary as long as the path points to an available database somewhere on your system.

    3. Examine the GetInventory, the BindToInventory, and the Page_Load methods. Listing 14-1 shows the code.

      Listing 14-1

         protected DataTable GetInventory()
         {
         String strConnection =
            @"Provider=Microsoft.Jet.OLEDB.4.0;
            DataSource=
            c:\\inetpub\\wwwroot\\SessionState\\App_Data\\ASPDotNetStepByStep.mdb";
       
            DbProviderFactory f =
               DbProviderFactories.GetFactory("System.Data.OleDb");
      
            DbConnection connection = f.CreateConnection();
            connection.ConnectionString = strConnection;
       
            connection.Open();
            DbCommand command = f.CreateCommand();
            command.CommandText = "Select * from DotNetReferences";
            command.Connection = connection;
       
            IDataReader reader = command.ExecuteReader();
       
            DataTable dt = new DataTable();
            dt.Load(reader);
            reader.Close();
            connection.Close();
            connection.Dispose();
       
            return dt;
         }
         protected DataTable BindToInventory()
         {
            DataTable dt;
            dt = this.GetInventory();
            this.DataList1.DataSource = dt;
            this.DataBind();
            return dt;
         }
       
       protected void Page_Load(object sender, EventArgs e)
         {
       
          if (!IsPostBack)
            {
              DataTable dt = BindToInventory();
              DataTable tableSelectedItems =
                 this.CreateSelectedItemsTable(dt);
                 Session["tableSelectedItems"] = tableSelectedItems;
              }
         }
    4. Run the application to make sure it works. That is, it should connect to the DotNetReferences table and bind the DataList to the table from the database.

      The GetInventory and BindToInventory methods are called by the Page_Load method. How often is Page_Load called? Every time a new page is created¡ªwhich happens for every single HTTP request destined for the UseDataList page. In the case of running this application on a single computer with one client (in a testing situation), perhaps connecting to the database for every request isn't a big deal. However, for applications expecting to serve thousands of users making frequent requests, repeated database access actually becomes a very big deal. Accessing a database is actually a very expensive operation. As we'll see shortly, it may take up to a half second to simply connect to this access database and read the mere 25 rows contained in the DotNetReferences table. Data access can only get more expensive as the size of the tables in the database grows. A half-second in the computer processing time scale is eons to the program.

      Now think about the nature of the inventory table. Does it change often? Of course, not in the case of this simple application. However, think about how this might work in a real application. The items carried within an inventory will probably not change often (and such changes will probably occur at regular, predictable intervals). If that's the case, why does the application need to hit the database each time a page is loaded? Doing so is certainly overkill. If you could take that data and store it in a medium offering quicker access than the database (for example, the computer's internal memory), your site could potentially serve many more requests than if it had to make a round-trip to the database every time it loads a page. This is a perfect opportunity to cache the data.


    Using the data cache in the simplest and most nave way supported by ASP.NET is very like accessing the Session object. Remember, accessing the Session object involves using an indexer (the square brace syntax) and a consistent index to store and retrieve data. The data cache works in exactly the same way (although it has some other features for managing items in the cache).

    The strategy for caching a piece of data usually involves these steps:

    1. Look in the cache for the data element.

    2. If it's there, use it (bypassing the expensive database round-trip).

    3. If the data is unavailable in the cache, make a round-trip to the database to fetch it.

    4. Cache the data so it's available next time around.

    The next example modifies the UseDataList page so that it stores the data in the cache after acquiring it for the first time. While the first time Page_Load is called, subsequent calls are infinitely faster.

    Using the Cache

    1. Open the UseDataList.aspx.cs file and go to the GetInventory method.

    2. Modifying the method to use the cache is fairly straightforward. The following listing highlights the changes. First, check to see if the item is in the cache. If searching the cache for the DataSet turns up a valid pointer, then you may bypass the database lookup code and return the DataSet. If searching the cache turns up a null pointer, go ahead and make the round-trip to the database. When the database lookup finishes, you'll have a good DataSet. Cache it before returning the reference to the caller. If you include the Trace statements, you'll be able to see exactly how big an impact caching can make.

      protected DataTable GetInventory()
      {
         DataTable dt;
      
      Trace.Warn("Page_Load", "looking in cache");
      C:dt = (DataTable)Cache["InventoryDataTable"];
      Trace.Warn("Page_Load", "done looking in cache");
      
        if (dt == null)
        {
      
            Trace.Warn("Page_Load", "Performing DB lookup");
      
              dt = new DataTable();
      
              String strConnection =
              @"Provider=Microsoft.Jet.OLEDB.4.0;
              DataSource=c:
          \\inetpub\\wwwroot\\SessionState\\App_Data\\ASPDotNetStepByStep.mdb";
      
             DbProviderFactory f =
             DbProviderFactories.GetFactory("System.Data.OleDb");
      
             DbConnection connection = f.CreateConnection();
             connection.ConnectionString = strConnection;
      
             connection.Open();
             DbCommand command = f.CreateCommand();
             command.CommandText = "Select * from DotNetReferences";
             command.Connection = connection;
      
             IDataReader reader = command.ExecuteReader();
      
             dt.Load(reader);
      
             reader.Close();
             connection.Close();
             connection.Dispose();
      
           Cache["InventoryDataTable"] = dt;
           Trace.Warn("Page_Load", "Done performing DB lookup");
      
         }
         return dt;
      }

      This code reduces the cost of loading the page significantly (after the data is loaded in the cache, of course). Next time the page is loaded, it'll use the cached version¡ªavailable through Cache at a tremendously reduced cost. How much is the cost savings? It's huge¡ªas you can see looking at the trace pages for the application. Let's take a peek.

    Impact of Caching

    If you included the Trace statements in the GetInventory method, then you can surf to the trace page to see the effect of caching. The UseDataCaching application included here has the Trace attribute turned off in the page, but has application tracing turned on. That is, the Web.Config includes the following section:

    <configuration>
       <system.web>
       <trace enabled="true" />
       <system.web>
    </configuration>

    You can see the trace information by surfing to the virtual directory with a file name of Trace.axd. Here's the URI for following the trace information:

    http://localhost/UseDataCaching/trace.axd

    Figure 14-1 shows the trace statements produced by accessing the page for the first time. The column furthest to the right indicates the time elapsed since the previous trace statement. The trace statement shows that 0.558656 seconds passed while the page was loading the DataSet. That's over a half-second.

    Figure 14-1 Hitting the database takes over a half second in this scenario.
    Figure 14-1 Hitting the database takes over a half second in this scenario.

    Make a few more posts to the page (for example, add some items from the inventory to the selected items grid). Then go back and look at the tracing information for the subsequent post-backs. Figure 14-2 shows some example trace statements. Fetching from the Cache is dramatically faster than hitting the database¡ªby several orders of magnitude! Again, you may not notice the difference with just one client surfing the page every once in a while. However, when multiple clients are surfing to the same page simultaneously, they'll get their responses much more quickly than if the page had to make a round-trip to the database.

    Figure 14-2 Fetching data from the cache takes 0.000039 seconds.
    Figure 14-2 Fetching data from the cache takes 0.000039 seconds.

    The last example cached items in the most nave way possible. They were simply placed in the cache and given an index. However, at times you may need a bit more control over the items in the cache. For example, what if the physical source backing one of the items you cache changes? If getting accurate information out to your users is important, you may want to know about the change so you can handle it (perhaps by reloading the new information into the cache). As another example, what if you knew that the data in your cache would become invalid after a certain period of time, or on a certain date? You'd want to make sure the data in the cache is invalidated and appropriately refreshed with new data.

    In addition to placing items in the cache using the indexer, the Cache object implements a parameterized method named Insert that allows you control over many aspects of the cached item. The ways in which you may control cache entries include the following:

    • Setting up an absolute expiration time

    • Setting up a sliding expiration time

    • Setting up dependencies between cached items and their backing sources (for example database, file, directory dependencies, or dependencies upon other cache entries)

    • Managing a relative priority of cached items

    • Setting up callback functions to be called when items are removed

    The Cache's insert method includes four overloads. Table 14-1 enumerates them.

    Table 14-1 Overloads for the Cache.Insert method

    Insert Overload

    Description

    Insert (String, Object)

    Directly corresponds to the indexer version. Blindly places the object in the Cache using the string key in the first parameter.

    Insert (String, Object, CacheDependency)

    Inserts an object into the Cache and associates it with a dependency.

    Insert (String, Object, CacheDependency, DateTime, TimeSpan)

    Inserts an object into the Cache, associating it with a dependency and an expiration policy.

    Insert (String, Object, CacheDependency, DateTime, TimeSpan, CacheItemPriority, CacheItemRemovedCallback)

    Inserts an object into the Cache. Associates a dependency and expiration and priority policies. Also associates the Cache entry with a delegate for a callback to notify the application when the item is removed from the cache.

    The following example illustrates some of these settings and how they work. In addition, the forthcoming examples illustrate another way to get DataTables and DataSets. You may actually create them programmatically. The next few examples use a DataTable that is created in memory rather than being fetched from a database. While the impact of caching isn't quite as dramatic when using the in-memory DataTable, it is still appreciable¡ªand you can see this other approach to managing data. We'll also see how the DataTable serializes as XML as well (which will be useful for examining cached items with file dependencies).

    DataSets in Memory

    In Tutorial 11, we looked at making a round-trip to the database to gather data suitable to bind to a control. Then we looked at maintaining data between requests by using the Session object. The Session object holds any .NET CLR object¡ªeven a DataReader. However, it's not a good idea to hold on to a DataReader for long periods of time as that means holding a connection open. Having too many open connections will ultimately slow your site to a crawl. A better approach is to make single round-trips to the database and hold on to a DataTable or a DataSet.

    In addition to fetching them from databases, a DataTable may be synthesized programmatically. Doing so involves constructing a DataTable and adding DataRows to describe the schema. After constructing a DataTable, you may use it to create columns with the correct ¡°shape,¡± populate them, and then add them to the table's columns collection. Listing 14-2 shows an example of creating a DataTable in memory. The table is a collection of famous quotes and their originators that will be useful in the next examples.

    Listing 14-2

      public class QuotesCollection : DataTable
       {
          public QuotesCollection()
          {
                //
                // TODO: Add constructor logic here
                //
          }
     
        public void Synthesize()
          {
                // Be sure to give a name so that it will serialize as XML
                this.TableName = "Quotations";
                DataRow dr;
     
                Columns.Add(new DataColumn("Quote", typeof(string)));
                Columns.Add(new DataColumn("OriginatorLastName", typeof(strin)));
                Columns.Add(new DataColumn("OriginatorFirstName",
                typeof(string)));
     
                dr = this.NewRow();
                dr[0] = "Imagination is more important than knowledge.";
                dr[1] = "Einsten";
                dr[2] = "Albert";
                Rows.Add(dr);
     
                dr = this.NewRow();
                dr[0] = "Assume a virtue, if you have it not";
                dr[1] = "Shakespeare";
                dr[2] = "William";
                this.Rows.Add(dr);
     
                dr = this.NewRow();
                dr[0] = @"A banker is a fellow who lends you his umbrella
                      when the sun is shining, but wants it back the
                      minute it begins to rain.";
                dr[1] = "Twain";
                dr[2] = "Mark";
                this.Rows.Add(dr);
     
                dr = this.NewRow();
                dr[0] = "A man cannot be comfortable without his own approval.";
                dr[1] = "Twain";
                dr[2] = "Mark";
                this.Rows.Add(dr);
     
                dr = this.NewRow();
                dr[0] = "Beware the young doctor and the old barber";
                dr[1] = "Franklin";
                dr[2] = "Benjamin";
                this.Rows.Add(dr);
     
                dr = this.NewRow();
                dr[0] = @"Reality is merely an illusion, albeit a
                           very persistent one.";
                dr[1] = "Einstein";
                dr[2] = "Albert";
                this.Rows.Add(dr);
     
                dr = this.NewRow();
                dr[0] = "Beer has food value, but food has no beer value";
                dr[1] = "Sticker";
                dr[2] = "Bumper";
                this.Rows.Add(dr);
     
                dr = this.NewRow();
                dr[0] = @"Research is what I'm doing when I don't know
                              what I'm doing";
                dr[1] = "Von Braun";
                dr[2] = "Wernher";
                this.Rows.Add(dr);
     
                dr = this.NewRow();
                dr[0] = "Whatever is begun in anger ends in shame";
                dr[1] = "Franklin";
                dr[2] = "Benjamin";
                this.Rows.Add(dr);
     
                dr = this.NewRow();
                dr[0] = "We think in generalities, but we live in details";
                dr[1] = "Whitehead";
                dr[2] = "Alfred North";
                this.Rows.Add(dr);
     
                dr = this.NewRow();
                dr[0] = "Every really new idea looks crazy at first.";
                dr[1] = "Whitehead";
                dr[2] = "Alfred North";
                this.Rows.Add(dr);
     
                dr = this.NewRow();
                dr[0] = @"The illiterate of the 21st century will not be
                      those who cannot read and write, but those who cannot learn,
                      unlearn, and relearn.";
                dr[1] = "Whitehead"";
                dr[2] = "Alfred North";
                this.Rows.Add(dr);
     
        }
       }

    Building a DataTable in memory is straightforward¡ªit's mostly a matter of defining the column schema and adding rows to the table.

    Now let's take a look at managing items within the cache.

    Cache Expirations

    The first way of managing cached items is to give them expiration thresholds. In some cases, you may be aware of certain aspects of your cached data that allow you to place expiration times on it. The Cache supports both absolute expirations and sliding expirations.

    Absolute Expiration

    1. To try out absolute expirations, add a new page to the UseDataCaching site named CacheExpirations.

    2. Use the Website | Add Existing Item to bring the QuoteCollection.cs file .

    3. Drag a GridView onto the CacheExpirations page. Don't bind it to a data source yet. We'll handle that in the Page_Load method.

      Graphic
    4. In the Page_Load method of the CacheExpirations page, check the cache to see if there's already an instance of the QuoteCollections object (just as in the previous example). If the data's not available from the cache, create an instance of the QuoteCollections class and call the Synthesize method to populate the table. Finally, add it to the cache using the overloaded Insert method. You can use the DataTime class to generate an absolute expiration. Bind the QuotesCollection object to the GridView. The caching policy should be Cache.NoSlidingExpiration. Set up some trace statements so you may see how the expiration times affect the lifetime of the cached object.

          protected void Page_Load(object sender, EventArgs e)
          {
               QuotesCollection quotesCollection;
      
               DateTime dtCurrent = DateTime.Now;
               Trace.Warn("Page_Load",
      "Testing cache at: " +
      dtCurrent.ToString());
               quotesCollection = (QuotesCollection)Cache["QuotesCollection"];
      
               if (quotesCollection == null)
               {
      
                     quotesCollection = new QuotesCollection();
                     quotesCollection.Synthesize();
      
                     DateTime dtExpires = new DateTime(2005, 12, 31, 23, 59, 59);
                     dtCurrent = DateTime.Now;
      
                     Trace.Warn("Page_Load",
      "Caching at: " +
      dtCurrent.ToString());
                     Trace.Warn("Page_Load",
      "This entry will expire at: " + 
      dtExpires);
                     Cache.Insert("QuotesCollection",
                              quotesCollection,
                              null,
                              dtExpires,
                              Cache.NoSlidingExpiration,
                              CacheItemPriority.Default,
                                 null);
      }
      
               this.GridView1.DataSource = quotesCollection;
               this.DataBind();
      }
    5. Experiment with changing the dates and times to see how setting the expiration time forces a reload of the cache.

      An absolute expiration time applied to the cached item tells ASP.NET to flush the item from the cache at a certain time. Now let's try using a different kind of expiration technique¡ªthe sliding expiration. Using a sliding expiration tells ASP.NET to keep the data in the cache as long as it has been accessed within a certain period of time. Items that have not been accessed within that time frame are subject to expiration.

    Sliding Expirations

    1. Now try setting a sliding expiration for the cached data. Modify the Page_Load method in the CacheExpirations page. Getting a sliding expiration to work is simply a matter of changing the parameters of the Insert method. Make up a time span after which you want the cached items to expire. Pass DateTime.MaxValue as the absolute expiration date and the timespan as the final parameter like so:

          protected void Page_Load(object sender, EventArgs e)
          {
               QuotesCollection quotesCollection;
      
               DateTime dtCurrent = DateTime.Now;
               Trace.Warn("Page_Load",
                     "Testing cache: " + dtCurrent.ToString());
                quotesCollection =
                     (QuotesCollection)Cache["QuotesCollection"];
      
               if (quotesCollection == null)
               {
                     quotesCollection = new QuotesCollection();
                     quotesCollection.Synthesize();
      
                     TimeSpan tsExpires = new TimeSpan(0, 0, 15);
                     dtCurrent = DateTime.Now;
      
                     Trace.Warn("Page_Load",
                           "Caching at: " + dtCurrent.ToString());
                     Trace.Warn("Page_Load",
                           "This entry will expire in: " +
                           tsExpires.ToString());
                     Cache.Insert("QuotesCollection",
                           quotesCollection,
                              null,
                              DateTime.MaxValue,
                              tsExpires);
               }
      
               this.GridView1.DataSource = quotesCollection;
               this.DataBind();
         }
    2. Surf to the page. You should see the cache reloading if you haven't accessed the cached item within the designated time frame.

      Cache dependencies represent another way to manage cached items. Let's take a look at how they work.

    Cache Dependencies

    In addition to allowing objects in the cache to expire by duration, you may set up dependencies for the cached items. For example, imagine our program loads some data from a file and places it into the cache. The backing file (that is, the source of the cached information) may change, making the data in the cache invalid. ASP.NET supports setting up a dependency between the cached item and the file so that changing the file invalidates the cached item. The conditions under which the cached items may be flushed include when a file changes, a directory changes, another cache entry is removed, or data in a table in an SQL Server changes (this is an often requested feature finally available in ASP.NET 2.0!).

    Here's an example that illustrates setting up cache dependencies.

    Setting Cache Dependencies

    1. Add a new page to the UseDataCache site. Name it CacheDependencies.aspx.

    2. Place a button on the page that you may use to post a request to the page to generate an XML file from the QuotationsCollection. Also, drag a GridView onto the page like so:

      Graphic
    3. Double-click the button to generate a handler for the button that will save the XML Schema and the XML from the DataTable to .XML and .XSD files in the App_Data directory.

    4. Within the handler, instantiate a QuotesCollection object and call Synthesize to generate the data. Within the page, you have a reference to the Server object. Call the MapPath method in the Server object to get the physical path for saving the file. Then use that path to create an XML file and a schema file. The DataTable will do this for you automatically by calling the WriteXmlSchema and WriteXml methods, respectively.

      protected void ButtonSaveAsXML_Click(object sender, EventArgs e)
      {
      
         QuotesCollection quotesCollection = new QuotesCollection();
         quotesCollection.Synthesize();
         String strFilePathXml =
         Server.MapPath(Request.ApplicationPath +
         "\\app_data\\QuotesCollection.xml");
         String strFilePathSchema =
         Server.MapPath(Request.ApplicationPath +
         "\\app_data\\QuotesCollection.xsd");
         quotesCollection.WriteXmlSchema(strFilePathSchema);
         quotesCollection.WriteXml(strFilePathXml);
      
      }
    5. Now write a method to load the XML into the QuotationsCollection object and cache the data. You can use the file path to the XML file to create a dependency on the file. When it changes, ASP.NET will empty the cache. Turn off the absolute expiration and the sliding expiration by passing in Cache.NoAbsoluteExpiration and Cache.NoSlidingExpiration. If you put trace statements in, you can see the effect of updating the file after it's been loaded in the cache. Finally, make sure to bind the GridView to the QuotationCollection.

      protected void CacheWithFileDependency()
      {
         QuotesCollection quotesCollection;
      
         Trace.Warn("Page_Load", "Testing cache ");
         quotesCollection = (QuotesCollection)Cache["QuotesCollection"];
      
         if (quotesCollection == null)
         {
               Trace.Warn("Page_Load", "Not found in cache");
               quotesCollection = new QuotesCollection();
      
               String strFilePathXml =
                     Server.MapPath(Request.ApplicationPath +
                     "\\app_data\\QuotesCollection.xml");
               String strFilePathSchema =
                     Server.MapPath(Request.ApplicationPath +
                     "\\app_data\\QuotesCollection.xsd");
      
               quotesCollection.ReadXmlSchema(strFilePathSchema);
               quotesCollection.ReadXml(strFilePathXml);
      
               CacheDependency cacheDependency =
                       new CacheDependency(strFilePathXml);
      
               Cache.Insert("QuotesCollection",
                               quotesCollection,
                               cacheDependency,
                               Cache.NoAbsoluteExpiration,
                               Cache.NoSlidingExpiration,
                               CacheItemPriority.Default,
                                  null);
         }
      
         this.GridView1.DataSource = quotesCollection;
         this.DataBind();
      }
    6. Call the CacheWithFileDependency() within the Page_Load method.

      protected void Page_Load(object sender, EventArgs e)
      {
         CacheWithFileDependency();
      }
    7. Now run the page. It should load the XML and schema into the QuotesCollection, save the QuotesCollection in the cache, and then show the data in the grid. Clicking the Save As XML button will refresh the XML file (upon which a cache dependency was made). Because the file on the disk changes, ASP.NET will flush the cache. Next time you load the page, the cache will need to be reloaded.

    Now let's look at the final cache dependency: the SQL Server dependency.

    The SQL Server Dependency

    ASP.NET 1.0 had a huge gap in its cache dependency functionality. The most useful type of dependency was completely missing¡ªthat is, a dependency between a cached item coming from SQL Server and the physical database. Because so many sites use data provided by SQL Server to back their DataGrids and other controls, establishing this dependency is definitely a most useful way to manage cached data.

    For the SQL Server dependency to work, you first configure SQL Server using the program aspnet_regsql.exe. The dependency is described in the configuration file, whose name is passed into the SqlCacheDependency constructor. The SqlCacheDependency class monitors the table. When something causes the table to change, ASP.NET will remove the item from the Cache.

    Listing 14-3 shows a configuration file with a dependency upon SQL Server. Listing 14-4 shows an ASP.NET page that loads the data from the SQL Server database and establishes a dependency between the database and the cached item.

    Listing 14-3

     
     <caching>
        <sqlCacheDependency enabled="true" >
          <databases >
             <add name="DBName" pollTime="500"
                 connectionStringName="connectionString"/>
          </databases>
        </sqlCacheDependency>
       </caching>

    Listing 14-4

    <%@ Page Language="C#" %>
    <script runat="server">
        protected void Page_Load(Object sender, EventArgs e)
        {
            DataSet ds = null;
            ds = (DataSet)Cache["SomeData"];
            if (ds == null)
            {
                string cconnectionString =
                   ConfigurationSettings.ConnectionStrings["connectionString"].
                   ConnectionString;
                   SqlDataAdapter da =
                      new SqlDataAdapter("select * from DBName.table",
                      connectionString);
                   ds = new DataSet();
                   da.Fill(ds);
                   SqlCacheDependency sqlCacheDependency =
                      new SqlCacheDependency("DBName", "table");
                  Cache.Insert("SomeData",
                                  ds,
                                  sqlCacheDependency);
            }
            GridView1.DataSource = ds;
            DataBind();
        }
    </script>
    <html><body>
        <form id="form1" runat="server">
              <asp:GridView ID="GridView1" Runat="server">
            </asp:GridView>
        </form>
    </body></html>

    Once items are in the cache and their lifetimes are established through expirations and cached item dependencies, one other way to manage the cache remains¡ªreacting when items are removed.

    Clearing the Cache

    As you can see from the previous examples, ASP.NET clears the cache on several occasions by

    • removing items explicitly by calling Cache.Remove

    • removing low-priority items due to memory consumption

    • removing items that have expired

    One of the parameters to one of the Insert overloaded methods is a callback delegate so that ASP.NET can tell you that something's been removed from the cache. To receive callbacks, you simply need to implement a method that matches the signature, wrap it in a delegate, and then pass it when calling the Insert method. When the object is removed, ASP.NET will call the method you supply.

    The next example illustrates setting up a removal callback function.

    Removal Callback

    1. One of the main tricks to getting the removal callback to work is finding an appropriate place to put the callback. What happens if you make the callback a member of your Page class? It won't work. The callback will become disconnected after the first page has come and gone. The callback has to live in a place that sticks around. The perfect class for establishing the callback is in the global application area. We'll see the application services in more detail in Tutorial 17. For now, add a global application object to your application. Select Website | Add New Item. Find the Global Application template and insert it into the project. Visual Studio will add a new file named Global.asax to your application.

    2. Global.asax will include a server-side script block. Write a method to handle the callback within the Global.asax file. In this case, the response will be to set a flag indicating the cache is dirty. Then the code will simply place the data back into the cache during the Applicationi_BeginRequest handler. The code for doing so will look very much like the code in the CacheWithFileDependency method shown earlier. You can get a reference to the cache through the current HttpContext.

      <%@ Application Language="C#" %>
      
      <script runat="server">
      
         bool  _bReloadQuotations = false; 
      
         public void OnRemoveQuotesCollection(string key, object val,
               CacheItemRemovedReason r)
          {
              // Do something about the dependency Change
              if (r == CacheItemRemovedReason.DependencyChanged) 
            {
                  _bReloadQuotations = true; 
            }
          }
      
          protected void Application_BeginRequest(object sender, EventArgs e)
          {
              if (_bReloadQuotations == true) 
            {
                  ReloadQuotations();
                  _bReloadQuotations = false; 
            }
          }
      
         protected void ReloadQuuotations() 
         {
              QuotesCollection quotesCollection = new QuotesCollection();
      
            String strFilePathXml = 
                  Server.MapPath(HttpContext.Current.Request.ApplicationPath +
                  "\\app_data\\QuotesCollection.xml"); 
            String strFilePathSchema = 
                  Server.MapPath(HttpContext.Current.Request.ApplicationPath +
                  "\\app_data\\QuotesCollection.xsd"); 
      
            quotesCollection.ReadXmlSchema(strFilePathSchema);       
            quotesCollection.ReadXml(strFilePathXml);
      
            System.Web.Caching.CacheDependency  
                cacheDependency =
                new System.Web.Caching.CacheDependency(strFilePathXml); 
      
            HttpContext.Current.Cache.Insert("QuotesCollection",			
                quotesCollection,
                cacheDependency,
                Cache.NoAbsoluteExpiration,
                Cache.NoSlidingExpiration,
                CacheItemPriority.Default,
      
                this.OnRemoveQuotesCollection);
                  } 
           </script>
    3. Update the CacheWithFileDepenedency method to use the callback method when establishing the QuotesServer in the cache. You may access the callback method through the page's Application member.

         protected void CacheWithFileDependency()
         {
      
               QuotesCollection quotesCollection;
      
               Trace.Warn("Page_Load", "Testing cache ");
               quotesCollection = (QuotesCollection)Cache["QuotesCollection"];
      
               if (quotesCollection == null)
               {
                     Trace.Warn("Page_Load", "Not found in cache");
                     quotesCollection = new QuotesCollection();
      
                     String strFilePathXml =
                           Server.MapPath(Request.ApplicationPath +
                           "\\app_data\\QuotesCollection.xml");
                     String strFilePathSchema =
                           Server.MapPath(Request.ApplicationPath +
                           "\\app_data\\QuotesCollection.xsd");
      
                     quotesCollection.ReadXmlSchema(strFilePathSchema);
                     quotesCollection.ReadXml(strFilePathXml);
                     CacheDependency cacheDependency =
                          new CacheDependency(strFilePathXml);
      
                    Cache.Insert("QuotesCollection",
                             quotesCollection,
                             cacheDependency,
                             Cache.NoAbsoluteExpiration,
                             Cache.NoSlidingExpiration,
                             CacheItemPriority.Default,
                          this.ApplicationInstance.OnRemoveQuuotesCollection);
               }
      
               this.GridView1.DataSource = quotesCollection;
               this.DataBind();
      }

    When you surf to the page, you should never see the Page_Load method refreshing the cache. That's because when the XML file is overwritten, ASP.NET immediately calls the ReloadQuotations method¡ªwhich loads the cache again.

    Caching is one of the easiest and most well-understood ways of wringing better performance out of an application. ASP.NET implements an easy-to-use application cache. The application cache stores any CLR object and is available at any time while processing a request. You can dig it out of the current context (the HttpContext), and it's also available as a member variable of System.Web.UI.Page.

    Probably the most common way to use the cache is to store database query results to avoid round-trips to a database. Accessing memory is often orders of magnitude faster than hitting the database. In addition, you sidestep issues such as limited connection resources and database contention.

    While you can effectively improve the performance of your application by simply putting items in the cache, ASP.NET's caching mechanism provides facilities for putting limits on the amount of time items remain cached. You may also set up dependencies between cached items and their physical data sources so that you may be alerted when items need to be reloaded into the cache.

    To

    Do This

    Access the data cache

    The data cache is available as

    1. the Cache property in the page

    2. the Cache property in the current HttpContext

    Insert an item in the cache

    Use the indexer notation to add an object and a value to the cache

    Insert an item in the cache with a dependency

    Create a CacheDependency object and add the object to the cache using the overloaded insert method

    Insert an item in the cache with an expiration time

    Create a DateTime object and add the object to the cache using the overloaded insert method

    Delete an item from the cache

    Call the cache's Remove method

    Be notified that an item is being removed from the cache

    discuss this topic to forum

    relation tutorial

    No relevant information

    Category

      NET (110)

    New

    Hot