WEBSITE

Managing the Cache

9/25/2010 11:39:11 AM
In addition to placing items in the cache using the indexer, the Cache object implements a parameterized method named Insert that you can use to control many aspects of the cached item. The ways in which you can 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, or directory dependencies, or even dependencies on other cache entries)

  • Managing a relative invalidation priority of cached items

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

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

Table 1. Overloads for the Cache.Insert Method
Insert OverloadDescription
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 can actually create them programmatically. The next few examples use a DataTable that is created in memory rather than being fetched from a database. Although 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. The following section also shows how the DataTable serializes as XML as well (which is useful for examining cached items with file dependencies).

1. DataSets in Memory

In addition to fetching them from databases, you can synthesize a DataTable programmatically. Doing so involves constructing a DataTable and adding DataRows to describe the schema. After constructing a DataTable, you can use it to create columns with the correct "shape," populate them, and then add them to the table's columns collection. Example 1 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.

Example 1. The QuotesCollection object
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(string)));
Columns.Add(new DataColumn("OriginatorFirstName",
typeof(string)));


dr = this.NewRow();
dr[0] = "Imagination is more important than knowledge.";
dr[1] = "Einstein";
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. This class is available on the CD accompanying this book, so you don't need to type the whole thing. You can just import it into the next examples.

The next section looks at managing items in the cache.

2. Cache Expirations

The first way to manage cached items is to give them expiration thresholds. In some cases, you might 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.

Placing absolute expirations

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

  2. Use Website, Add Existing Item to bring the QuoteCollection.cs file from the CD accompanying this book and make it part of this project.

  3. Drag a GridView onto the CacheExpirations page, as shown in the following graphic. Don't bind it to a data source yet. You handle that in the Page_Load method.



  4. In the Page_Load method of the CacheExpirations page, check the cache to see whether there's already an instance of the QuotesCollection object (just as in the previous example). If the data set is not available from the cache, create an instance of the QuotesCollection 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 DateTime 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 that you can 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(2008, 5, 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,
    System.Web.Caching.Cache.NoSlidingExpiration,
    System.Web.Caching.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 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.

Placing sliding expirations

  1. To set 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. Look at how they work next.

3. Cache Dependencies

In addition to allowing objects in the cache to expire by duration, you can set up dependencies for the cached items. For example, imagine your program loads some data from a file and places it into the cache. The backing file (that is, the source of the cached information) might 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 Microsoft SQL Server changes (this is an often requested feature available since ASP.NET 2.0).

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

Setting up cache dependencies

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

  2. Place a button on the page that you can use to post a request to the page to generate an XML file from the QuotesCollection. Use ButtonSaveAsXML as its ID. Also, drag a GridView onto the page like so:



  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. In the handler, instantiate a QuotesCollection object and call Synthesize to generate the data. In 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 DataTableWriteXmlSchema and WriteXml methods, respectively. will do this for you automatically by calling the

    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 QuotesCollection 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 QuotesCollection.

    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);
    System.Web.Caching.CacheDependency cacheDependency =
    new System.Web.Caching.CacheDependency(strFilePathXml);

    Cache.Insert("QuotesCollection",
    quotesCollection,
    new
    System.Web.Caching.CacheDependency(strFilePathXml),
    System.Web.Caching.Cache.NoAbsoluteExpiration,
    System.Web.Caching.Cache.NoSlidingExpiration,
    System.Web.Caching.CacheItemPriority.Default,
    null);
    }

    this.GridView1.DataSource = quotesCollection;
    this.DataBind();
    }


  6. Call the CacheWithFileDependency() in the Page_Load method.

    protected void Page_Load(object sender, EventArgs e)
    {
    if (!IsPostBack)
    {
    ButtonSaveAsXML_Click(null, null);
    }
    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 Table As XML button refreshes the XML file (on 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.

Next, look at the final cache dependency: the SQL Server dependency.

4. 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.

Example 2 shows a configuration file with a dependency on SQL Server.

Example 2. Configuration settings for SQL Server cache dependency
<caching>
<sqlCacheDependency enabled="true" >
<databases >
<add name="DBName" pollTime="500"
connectionStringName="connectionString"/>
</databases>
</sqlCacheDependency>
</caching>

Example 3 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.

Example 3. Page using SqlCacheDependency
<%@ Page Language="C#" %>
<%@ Import namespace="System.Data" %>
<%@ Import namespace="System.Data.SqlClient" %>
<script runat="server">
protected void Page_Load(Object sender, EventArgs e)
{
DataSet ds = null;
ds = (DataSet)Cache["SomeData"];
if (ds == null)
{
string connectionString =
ConfigurationManager.ConnectionStrings["connectionString"].
ConnectionString;
SqlDataAdapter da =
new SqlDataAdapter("select * from DBName.tableName",
connectionString);
ds = new DataSet();
da.Fill(ds);
SqlCacheDependency sqlCacheDependency =
new SqlCacheDependency("DBName", "tableName");
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 cache administrative task remains—reacting when items are removed.

5. Clearing the Cache

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

  • Removing items explicitly by calling Cache.Remove

  • Removing low-priority items because of 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.

Setting up a 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 normal instance 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. (You could make the callback a static method, however.) The perfect class for establishing the callback is in the global application class. Add a global application class to your application. Select Website, Add New Item. Select the Global Application Class template, and click Add to insert it into the project. Microsoft Visual Studio adds a new file named Global.asax to your application.

  2. Global.asax.cs includes application-wide code. Write a method to handle the callback in the Global.asax.cs 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 Application_BeginRequest handler. The code for doing so looks very much like the code in the CacheWithFileDependency method shown earlier. You can get a reference to the cache through the current HttpContext.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Security;
    using System.Web.SessionState;
    using System.Web.Caching;

    namespace UseDataCaching
    {
    public class Global : System.Web.HttpApplication
    {
    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 ReloadQuotations()
    {
    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,
    System.Web.Caching.Cache.NoAbsoluteExpiration,
    System.Web.Caching.Cache.NoSlidingExpiration,
    System.Web.Caching.CacheItemPriority.Default,
    this.OnRemoveQuotesCollection);
    }

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
    if (_bReloadQuotations == true)
    {
    ReloadQuotations();
    _bReloadQuotations = false;
    }
    }
    // VS-provided code
    }
    }


  3. Update the CacheWithFileDependency method to use the callback method when establishing the QuotesServer in the cache. You can 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);

    System.Web.Caching.CacheDependency cacheDependency =
    new System.Web.Caching.CacheDependency(strFilePathXml);

    Global global = HttpContext.Current.ApplicationInstance as Global;
    Cache.Insert("QuotesCollection",
    quotesCollection,
    cacheDependency,
    System.Web.Caching.Cache.NoAbsoluteExpiration,
    System.Web.Caching.Cache.NoSlidingExpiration,
    System.Web.Caching.CacheItemPriority.Default,
    global.OnRemoveQuotesCollection);
    }
    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.

Other  
 
Most View
Delete & Recover Data (Part 4) - Securely Deleting Data Using Eraser 6.0
Samsung ATIV S Review - A Strange Kind Of Windows Phone 8 Device (Part 2)
Html 5 : Text Tags and a Little CSS3 - Adding More HTML5 Structure
Dell Latitude 6430u - High-End Business Ultrabook
Android Application Development : Drawing 2D and 3D Graphics - Bling (part 4) - OpenGL Graphics
Nook HD - A High-Definition Tablet With The Heart Of A Reader (Part 1)
ASUS F2A85-M Pro - Reasonably Priced Motherboard
Best Photo Printers Revealed – Jan 2013 (Part 4) : Epson Stylus Photo R2000, Canon PIXMA Pro-1
Introducing HTML5 - Creating with Tags : An Overview
Life Logging - Is It Worth The Effort? (Part 6)
Top 10
Sharepoint 2013 : Farm Management - Disable a Timer Job,Start a Timer Job, Set the Schedule for a Timer Job
Sharepoint 2013 : Farm Management - Display Available Timer Jobs on the Farm, Get a Specific Timer Job, Enable a Timer Job
Sharepoint 2013 : Farm Management - Review Workflow Configuration Settings,Modify Workflow Configuration Settings
Sharepoint 2013 : Farm Management - Review SharePoint Designer Settings, Configure SharePoint Designer Settings
Sharepoint 2013 : Farm Management - Remove a Managed Path, Merge Log Files, End the Current Log File
SQL Server 2012 : Policy Based Management - Evaluating Policies
SQL Server 2012 : Defining Policies (part 3) - Creating Policies
SQL Server 2012 : Defining Policies (part 2) - Conditions
SQL Server 2012 : Defining Policies (part 1) - Management Facets
Microsoft Exchange Server 2010 : Configuring Anti-Spam and Message Filtering Options (part 4) - Preventing Internal Servers from Being Filtered