aching is one of the features in ASP.NET 1.x that most developers are familiar with. And it has helped to fuel a yearning for cache dependency in SQL Server. The ability to refresh the cache if a row in a table has been modified is very useful to a lot of developers. And hence in ASP.NET 2.0, Microsoft built SQL Server cache dependency right into the product.
In this article, I’ll look at the various ways you can cache your Web pages in ASP.NET 2.0; some of these techniques are even applicable in ASP.NET 1.x.
Technique #1: Page Output Caching
(ASP.NET 1.x and ASP.NET 2.0)
By caching the output of a page, the page need not be re-generated on every request; this is the basic logic behind page output caching. Suppose I have a Web form with a GridView control bound to a SqlDataSource control (see Figure 1).
|Author’s Note: If paging is enabled on the GridView control, do not enable page output caching; it can cause page-loading problems.|
When the page loads, I change the SelectCommand of the SqlDataSource control so that the GridView control displays rows based on the product name. The product name is retrieved from the query string of the URL. For example:
Here is the Page_Load event:
Sub Page_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles Me.Load Dim productName As String = _ Request.QueryString("productName") SqlDataSource1.SelectCommand = _ "SELECT * FROM Products WHERE ProductName LIKE '%" & _ productName & "%'" Response.Write("Page created on :" & Now)End Sub
To cache the output, add an “OutputCache” directive to this page:
<%@ OutputCache Duration="15" VaryByParam="none" %>
|Figure 1. Basic Cache: The following application, a Web form with a GridView and SqlDataSource controls, will make a good example for basic caching.|
The Duration attribute specifies that the page will be cached for 15 seconds. That is, once a page is loaded it will be cached for the next 15 seconds?any request for the page during this period will be served by the cache. The VaryByParam attribute specifies the condition for invalidating the cache. In this case, if it is set to “none,” the validity of the cache is not dependent on the query string. That is, no matter what you enter in the query string the cache will be valid. For example, you should see the same page if you enter the following URLs in succession (within the 15 seconds time frame):
To set a condition to invalidate the cache, specify the parameter name in the VaryByParam attribute. For example:
<%@ OutputCache Duration="15" VaryByParam="productName" %>
The above statement invalidates the cache if the productName parameter is changed. So the following URLs will cause the page to regenerate on every request:
If you have more than one parameter in the query string, separate the parameter name by a semi-colon, as in:
<%@ OutputCache Duration="15" VaryByParam="productName;price" %>
To invalidate the cache based on all parameters in the query string, use “*”:
<%@ OutputCache Duration="15" VaryByParam="*" %>
|What You Need|
Technique #2: Cache Data Using the Cache Object
(ASP.NET 1.x and ASP.NET 2.0)
Output caching has its applications but sometimes it’s more useful to explicitly cache the data that you would use frequently using the Cache object.
For example, suppose I have a text file named announcement.txt that contains messages to be displayed to the user when the main page of my Web site is loaded. Instead of loading this page every time a user visits my site (performing an I/O operation to read the announcement file is a computationally expensive task), I would cache the content of the file the first time it is read. However, the content of this file would change over time, and hence I need to have a method of invalidating the cache if the content of the file changes.
ASP.NET supports the CacheDependency class that allows the cache object to be linked to a dependency. In my case, the dependency is on the file announcement.txt.
Here is the code to read the announcement from the file, cache it, and add a dependency to it:
Sub Page_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles Me.Load displayAnnouncement()End SubPublic Sub displayAnnouncement() Dim announcement As String If Cache("announcement") Is Nothing Then Dim file As New _ System.IO.StreamReader _ (Server.MapPath("announcement.txt")) announcement = file.ReadToEnd file.Close() Dim depends As New _ System.Web.Caching.CacheDependency _ (Server.MapPath("announcement.txt")) Cache.Insert("announcement", announcement, depends) End If Response.Write(CType(Cache("announcement"), String))End Sub
When the page loads for the first time, the content of the file is cached. On a subsequent request, the content is taken from the cache. But if the content of the file has changed, the cache is invalidated and the file is read and cached again.
Expiration Dates: Invalidating the Cache
The previous example shows that the cache will only be invalidated when the content of the file changes. Another method to invalidate the cache is to use either Absolute Expiration or Sliding Expiration.
Absolute Expiration allows you to specify the duration of the cache, starting from the time the cache is activated. The following example shows that the cache has a cache dependency specified, as well as an expiration time of one minute (obviously, I’m using a one-minute cache merely for example purposes?you’ll want to set yours appropriately).
'===Absolute expiration===Cache.Insert("announcement", announcement, depends, _ DateTime.Now.AddMinutes(1), Nothing)
Sliding Expiration specifies that the cache will expire if a request is not made within a specified duration. Sliding expiration policy is useful whenever you have a large number of items that need to be cached, because this policy enables you to keep only the most frequently accessed items in memory. For example, the following code specifies that the cache will have a sliding duration of one minute. If a request is made 59 seconds after the cache is accessed, the validity of the cache would be reset to another minute:
'===Sliding expiration===Cache.Insert("announcement", announcement, depends, _ DateTime.MaxValue, _ TimeSpan.FromMinutes(1))
Technique #3: Caching Page Fragments
(ASP.NET 2.0 only)
|Figure 2. Two Timing: This page’s cache captures only some of the data.|
In ASP.NET 2.0, you now have the capability to cache parts of a page, rather than the entire page as discussed earlier. For example, the following code sample specifies caching the page output every 15 seconds. I’ve also included a shared function called RealTime (which takes in a System.Web.HttpContext parameter) that returns the time.
<%@ Page Language="VB" %><%@ OutputCache Duration="15" VaryByParam = "none" %>
If you were to load this page and refresh it a few times, you’d notice that the time written on the top line only updates every 15 seconds, regardless of how often you refesh, while the time on the second line updates every time you refresh (see Figure 2). The WriteSubstitution method (from the Response object) allows fragments of a page to be executed and the output inserted into the cache output page. Technique #4: Using SQL Server Cache Dependency
(ASP.NET 2.0 only)
In the earlier example, I showed how you can use cache dependency to invalidate the cache when the content of a file has changed. While this is useful, it has its limitations. The biggest one being that data is usually stored in a database such as SQL Server. Therefore, it’s more common to cache data from a database, rather than a text file.
ASP.NET 2.0 has a new facility, the SQL Cache Dependency, which allows you to invalidate your cache based on the changes in the database. To use it, you first need to prepare your database and tables for change notification using a tool called aspnet_regsql.exe. To run the tool, go to Start->Programs->Visual Studio 2005 Beta->Visual Studio Tools->Visual Studio .NET Whidbey Command Prompt.
The aspnet_regsql.exe is a mixed mode (both graphical and command-based) tool that allows configuring your SQL Server for use with your ASP.NET application. To see the various options available with it, use the “/?” option:
C:Program FilesMicrosoft Visual Studio 8VC>aspnet_regsql /?
There are two steps you need to take to enable SQL Cache Dependency:
- Enable the database for change notifications
- Enable the tables for change notifications
To enable a database, use the tool’s -ed option (see Figure 3) by entering the following on the command line:
C:Program FilesMicrosoft Visual Studio 8VC>aspnet_regsql -S localhost -E -d Northwind -ed
To enable the tables, use the tool’s -et option (see Figure 4) by entering the following on the command line:
C:Program FilesMicrosoft Visual Studio 8VC>aspnet_regsql -S localhost -E -t Products -d Northwind -et
The next step would be to enable SQL Cache Dependency in your application. To do so, you need to first modify Web.config file (note the addition in bold):
| Author’s Note: In Beta 1, the element name for the cache configuration is
|Figure 5. Cache Crash: The AspNet_SqlCacheTablesForChangeNotification table is where SQL Server checks for cache dependency.|
Here, I have specified a connection string that connects to the Northwind database. I have also specified that I want to enable SQL Server caching and indicate a polling time of 5 seconds (unit is in milliseconds). This is the frequency at which the ASP.NET runtime (actually it is a background thread that is spun off for this purpose) polls the database (the AspNet_SqlCacheTablesForChangeNotification file) for changes. As the file is small, this process is very efficient and will not slow down the system. Hence it makes sense to specify a low number so that the application is always displaying the most updated data.
Finally, modify the data source control properties (assuming you are using a SqlDataSource control here) on which you want to enable SQL Cache Dependency (see Figure 6):
- EnableCaching: “True”
- SqlCacheDependency: “Northwind:Products”
|Figure 6. Picture This: Here you can see how to enable a SqlDataSource control for SQL Cache Dependency.|
The SqlCacheDependency attribute has the format of: database:table.
To see if SQL Server Cache dependency works, modify the rows in the Products table and refresh the page a few times. The table will refresh every 5 seconds if there is a change in the related database.
In this article, you have seen a number of techniques for caching your Web pages to enhance its performance. Most notably in ASP.NET 2.0, you have the ability to fine-tune the caching options, such as fragment caching as well as SQL Server Cache Dependency. While we are still some time away from the release of ASP.NET 2.0, it is time to investigate the new features that will make your applications more powerful.