Caching Dynamic Pages with ASP.NET

Tweet

Interested in .NET? Don’t miss SitePoint’s .NET Feature Guide — it’s an excellent resource!

Caching is the method of saving data to some form of memory (generally computer, not yours) with the aim of speeding up the time it takes to access that data. When you access a page, your browser will cache the page on your computer so that if you pressed the back button, it wouldn’t have to download the page files again.

This isn’t a new concept — CPU manufacturers have been doing it for years — but caching dynamic Web pages has historically been a difficult and unmanageable task for developers. Many people accomplished caching using the Application State in ASP, or using third party software.

Why do you want to cache?

The attraction of caching is that it can increase performance of an application. Imagine a page that made a request to a database 30,000 times a day. With caching you could cut that down to 1 request a day, even once a week!

That’s the attraction of caching!

ASP.NET offers many different ways to cache information, and control when it happens and what is cached. Using HTTP 1.1 capable mechanisms, ASP.NET can cache pages in the browser (on the client’s computer really), proxy servers and the Web server on which the application resides. You can cache entire pages, sections of pages, and even objects you created in your code (DataSets, Web Services)! And all it takes is one line of code! This is possibly one of my favourite features of ASP.NET!

Due to the sheer size of ASP.NET’s caching abilities, I’m only going to cover caching pages here.

Prerequisites

I’ll assume that you have a basic knowledge of ASP.NET development, including a grasp on working with data, form authentication and working with SQL Server or MSDE.

Getting Started

As I said in the introduction, just about all caching can be done with one line of code, and that line is:

<%@ OutputCache Duration="60" VaryByParam="None" %>

The two properties defined in the tag are the only properties required. The Duration property defines, in seconds, how long the page is to be cached and the other, VaryByParam, defines a query string (both POST and GET) key that would automatically remove the page from the cache.

And that’s it! The page is cached. Though the line above is the most basic way to cache pages, but even this alone can start introducing better performance to your site.

The OutputCache directive has 3 other properties that can control the caching of the page:

  • Location
  • VaryByCustom
  • VaryByHeader

Each offers a different way to control the cached page, which means that caching in ASP.NET is very flexible!

Now, let’s go over each property in more detail.

Duration

As I already said, this controls how long the page is cached, provided it isn’t removed by something else. This property is defined in seconds, and is required. If Duration‘s left out, it will return a parse error (bad).

Location

This property defines where the page is to be cached. There are 5 options, all of which can be found in the OutPutCacheLocation enumeration, which is part of the System.Web.UI namespace.

  1. Any
    This is the default value. It basically will try to cache it wherever it can (and by that I mean in one of the locations listed below, not in your cupboard!).

  2. Client
    This option caches the page in the client’s browser.

  3. Downstream
    This caches the page in any HTTP 1.1 enabled device other than the original server. Usually includes a proxy server or the client.

  4. Server
    Caches the page in the server memory.

  5. None
    Disables output caching for the requested page, the same as leaving the tag out.

VaryByParam

This property lets you define what parameters, either sent through POST or GET, will remove the page from the cache. This will overwrite the Duration property and remove the page from the cache. More than one value can be defined by separating each with a comma, and a wildcard (*) can be used to account for every variation. So an example of the tag with one parameter is:

<%@ OutputCache Duration="30" VaryByParam="username" %> 
<html>
  <body>
     Page was cache at: <strong><%=dateTime.Now.toString("G")%></strong>
  </body>
</html>

Every time a different username is sent, the page will be removed from the cache. Try going to the page with no query string, taking note of the time. Now add ?username=bob at the end of the URL and the time will change. Remove the query string and return to the page, it will say the same time as it did the first time. And if you wait 30 seconds and refresh the page, the time will be different!

VaryByCustom

To make the Cache object even more flexible, Microsoft built in the ability to cache the page based on a string, which is then controlled from the actual code of the application.

It does have one "out of the box" property, Browser. When VaryByCustom is set to Browser the page is cached every time a different browser agent and major version number requests the page.

So if you had a page with this at the top of it:

<%@ OutputCache Duration="60" VaryByCustom="Browser" VaryByParam="none" %> 
<html>
  <body>
     Page was cache at: <strong><%=dateTime.Now.toString("G")%></strong>
  </body>
</html>

the time would only change when a different browser visited the page, or 60 seconds had passed.

You can also use a custom key and value by overriding the HTTPApplication.GetVaryByCustom method in the Global.asax file.

Ok. It’s time for the part where we have to think…

Let’s say you wanted to cache a page that passed on who was logged into your site. Let’s start by looking at the page we want to cache.

<%@ Page Language="VB" Debug="true" %>  
<%@ OutputCache Duration="300" Location="Server"  
VaryByCustom="userLogged" VaryByParam="None" %>  
<%@ import Namespace="System.Data" %>  
<%@ import Namespace="System.Data.SQLClient" %>  
<script runat="server">  
 
Dim pageTimeDate As DateTime  
 
Sub Page_Load(byVal obj As Object, byVal e As EventArgs)  
  userName.Text = Context.User.Identity.Name  
End Sub  
 
Sub logOut(ByVal obj As Object, ByVal e As EventArgs)  
  FormsAuthentication.SignOut()  
End Sub  
 
</script>  
<html>  
  <head>  
  </head>  
  <body>  
  <form runat="server">  
     Page cached: <%=DateTime.Now.toString("G")%>  
     <br />  
     Logged in as:  
     <asp:Label id="userName" runat="server">Label</asp:Label>  
     <br />  
     <asp:Button id="Button1" onclick="logOut" runat="server"  
Text="Log Out"></asp:Button>  
</form>  
</body>  
</html>

On the first line, we set the OutputCache directive’s VaryByCustom to userLogged. Using the Page_Load sub-routine, we display the current logged in user (using a label called username) and display the time. We also have a logout button, so we can easily log in as someone else to test the script.

Now we’ll look at the script to set the userLogged string.

As I said earlier, we have to override the HTTPApplication.GetVaryByCutom attribute, and we can do this in the global.asax file. Anyone that has worked with ASP extensively will be familiar with the global.asa file. This file, which resides in the root of your site, allows you to handle application level functions that need to be fired every time the application’s started, or a new session is created.

With ASP.NET, the global.asa file has been kept, but renamed to global.asax (and is known as the ASP.NET application file), but it essentially does the same thing. This file is completely optional, and you can find out more about it at the MSDN site.

Now, let’s move on with our code…

At the start of the file (the global.asax file), just after the <script> tag, add the following:

Public Overrides Function GetVaryByCustomString(ByVal   
currentContext As HTTPContext, ByVal customArgs As String)  
As String  
  Select CustomArgs  
     Case "userLogged"  
        Return "userLogged=" & Context.User.Identity.Name  
     Case Else  
        Return MyBase.GetVaryByCustomString(currentContext, customArgs)  
  End Select  
End Function

Firstly we override (hence the Overrides keyword) the GetVaryCustomString method. It has two parameters, the first being the HTTPContext (this contains all HTTP specific information about the page request), while the other is a string that represents the value of the VaryByCustom attribute. As our VaryByCustom attribute is userLogged, it will be passed to our function as a parameter when the page is requested. We use a Select statement to check what the customtArgs parameter is, and because of this, we can easily add other values, meaning that we can cache pages in a number of different ways.

If the string has been cached, this is considered a "hit" and the page is served from the cache. If the string hasn’t been cached, then this is considered — you guessed it — a "miss", and is cached.

And that’s all there is to it.

VaryByHeader

Every time you request a Web page, your browser sends a number of HTTP headers. You can customise caching according to what these headers are, so, for instance, you can cache pages on things like the Referer, or Accept-Language. You can find a list of HTTP Headers here. You’d use it like so:

<%@ OutputCache Duration="30" VaryByParam="none" VaryByHeader="Referer" %>

As with most of the information we’ve gone over so far, this is a simple attribute defined in the OutputCache tag! This simple example will cache pages for 30 seconds based on the Referer.

Finishing Up

As we’ve just seen, caching Web pages with ASP.NET is amazingly easy, accomplished with a simple line. With this glimpse of ASP.NET’s caching abilities, you can improve your Web application’s performance. But remember: what we covered in this article is only a small part of the whole caching namespace! Try some of the other objects, experiment away, and in no time at all your applications be working faster, more efficiently, and have a lighter server load!

This article is part of SitePoint’s .NET Feature Guide — an excellent resource for aspiring and experienced .NET developers. Don’t miss it!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments