The ASP programming model includes the Application object, which allows developers to place both simple data types and COM objects in memory on the Web server so they could be accessible on any page, regardless of the current user. While ASP’s Application object retains its original functionality in ASP.NET, it is not as flexible as the classes found in the System.Web.Caching namespace. Let's examine some common caching scenarios and learn how the .NET caching classes can be used to improve the performance of your Web sites.
ASP caching with the Application object
In ASP, you could use the Application object to initialize and store an ADO connection string in the global.asa file like this:
Application("Connect") = "server=.;database=Northwind;trusted_connection=yes"
In this case, the Connect object would be initialized when the Web application was first accessed and would remain available to all pages until the Web server was stopped. In addition, variables stored in the Application object could be locked and unlocked with the Lock and Unlock methods to protect them from possible errors due to being accessed concurrently.
The .NET caching classes provide access to the ASP.NET caching engine, which can improve performance in a number of ways, including caching the HTML output for an entire page, a part of the page (referred to as fragment caching), or even individual objects. By using the caching engine, you can automatically expire cached items, define a cached item that is dependant on other items, and even set up a callback so that your code is notified when the item is removed from the cache.
Let’s look at two simple but effective examples that illustrate declarative and programmatic control of the caching engine.
Assume you have an ASP.NET Web site that calls a third party XML Web service to retrieve some information and display it on the main page of the site. In this scenario, you can use fragment caching to cache the HTML output from a Web user control that actually calls the Web service and formats the returned XML. This can be accomplished by adding the OutputCache directive to the HTML for the Web user control as shown here:
<!—HTML goes here —>
In this case, the user control, which you can see in use on the Quilogy Web site, calls the kbAlertz Web service in its code-behind class to retrieve the most recent knowledge base articles from Microsoft. You’ll notice that the Duration attribute is set to 86,400 seconds, meaning that the output from the user control will be flushed from the cache after 24 hours.
By setting the VaryByParam attribute to None, I specify that only one version of the page will be placed in the cache. Alternatively, using an asterisk or one or more numeric values allows ASP.NET to create a version of the HTML fragment for each unique query string value or combination of values used when loading the page in which the control sits.
You can also specify additional attributes to cache based on:
- · HTTP headers (VaryByHeader).
- · Cache location (Location).
- · Values of controls present on the page (VaryByControl).
- · A developer-supplied algorithm (VaryByCustom).
Obviously, in this scenario the advantage to using caching includes not only vastly increasing rendering performance of the Web user control, but also reducing dependence on the XML Web service by only calling it once every 24 hours.
You can programmatically manipulate the cache to place individual data items in it, thereby reducing the number of calls required to a backend database. For example, suppose an ASP.NET Web page displayed data from a relational database that included lookup data populated in a drop-down list box. Since the lookup data is relatively static and is the same for all users, it is a good candidate for caching.
To access the ASP.NET caching engine programmatically, both the Page and UserControl classes—which are used to create the code-behind classes in a Web Form and Web User Control—include a Cache property that exposes the System.Web.Caching.Cache object. Using this property, a developer can write code to populate an item and place it in the cache, or simply pull it out of the cache if it already exists, as shown in Listing A.
In this case, the cache is first checked in the Load or Init event to see if the item corresponding with the Categories key is in the cache. If not, an ADO.NET DataTable is created by calling a method in a custom data access class called WebData (not shown).
What to cache?
Both DataTables and DataSets are good candidates to place in the cache since they are totally disconnected from the underlying data source from which they were populated.
Once the DataTable has been retrieved, it is placed in the cache using the Insert method. In this case, the item is added to the cache with no dependencies using an absolute expiration of six hours and disregarding the sliding expiration interval. You could use the CacheItemRemovedCallback delegate to create a callback to be notified when the cached item expires.
On the other hand, if the item is in the cache, it is simply pulled out and cast back to the appropriate type using the CType method and bound to a DropDownList control called selCategory.
An important tool for optimization
ASP.NET’s caching functionality provides an important tool for developers looking to optimize the performance of their Web sites. The simple examples I've shown here illustrate just a few of the ways you can improve your Web applications. I will cover more on this tool in a future article.