How to cache in ASP.NET

This step-by-step article describes how to control the caching of Web pages and data objects in ASP.NET. When you cache Web pages, you avoid re-creating information when you make a later request. Caching is an important technique for building high performance and scalable server applications. When you make the first request for the page, you can store data objects, pages, or part of the page to the memory. You can store these items on a Web server, on a proxy server, or on the browser.

ASP.NET provides easier methods to control caching. You can use the @ OutputCache directive to control page output caching in ASP.NET. Use the HttpCachePolicy class to store arbitrary objects, such as datasets, to server memory. You can store the cache in applications such as the client browser, the proxy server, and Microsoft Internet Information Services (IIS). By using the Cache-Control HTTP Header, you can control caching.

Cache ASP.NET pages

You can use the @ OutputCache directive to cache, or you can cache programmatically through code by using Visual Basic .NET or Visual C# .NET. The @ OutputCache directive contains a Location attribute. This attribute determines the location for cached item. You can specify the following locations:

  • Any – This stores the output cache in the client’s browser, on the proxy server (or any other server) that participates in the request, or on the server where the request is processed. By default, Any is selected.
  • Client – This stores output cache in the client’s browser.
  • Downstream – This stores the output cache in any cache-capable devices (other than the origin server) that participate in the request.
  • Server – This stores the output cache on the Web server.
  • None – This turns off the output cache.

The following are code samples for the @ OutputCache directive and equivalent programmatic code.
To store the output cache for a specified duration

Declarative Approach:
<%@ OutputCache Duration=”60″ VaryByParam=”None” %>
Programmatic Approach:

To store the output cache on the browser client where the request originated
Declarative Approach:
<%@ OutputCache Duration=”60″ Location=”Client” VaryByParam=”None” %>
Programmatic Approach:

To store the output cache on any HTTP 1.1 cache-capable devices including the proxy servers and the client that made request

Declarative Approach:
<%@ OutputCache Duration=”60″ Location=”Downstream” VaryByParam=”None” %>
Programmatic Approach:

To store the output cache on the Web server

Declarative Approach:
<%@ OutputCache Duration=”60″ Location=”Server” VaryByParam=”None” %>
Programmatic Approach:
TimeSpan freshness = new TimeSpan(0,0,0,60);
DateTime now = DateTime.Now;

To cache the output for each HTTP request that arrives with a different City:

Declarative Approach:
<%@ OutputCache duration=”60″ varybyparam=”City” %>
Programmatic Approach:
Response.Cache.VaryByParams[“City”] = true;

For the VaryByCustom attribute, the VaryByHeader attribute, and the VaryByParam attribute in the @ OutputCache directive, the HttpCachePolicy class provides the VaryByHeaders property and the VaryByParams property, and the SetVaryByCustom method.

Turn off client and proxy caching
To turn off the output cache for an ASP.NET Web page at the client location and at the proxy location, set the Location attribute value to none, and then set the VaryByParam value to none in the @ OutputCache directive. Use the following code samples to turn off client and proxy caching.

Declarative Approach:
<%@ OutputCache Location=”None” VaryByParam=”None” %>
Programmatic Approach:

Cache arbitrary objects in server memory
ASP.NET includes a powerful, easy-to-use caching mechanism that you can use to store objects that require a lot of server resources to create in memory. The Cache class implements this method. Instances are private to each application and the lifetime is tied to the corresponding application.



Understanding ASP.NET Dynamic Compilation

In order for your Web application to service requests, ASP.NET must first parse and compile the code of your Web application into one or more assemblies. When the code is compiled, it is translated into a language-independent and CPU-independent representation called Microsoft Intermediate Language (MSIL). At run time, MSIL runs in the context of the .NET Framework, which translates MSIL into CPU-specific instructions for the processor on the computer running the application.

ASP.NET dynamic compilation enables you to modify your source code without having to explicitly compile your code before you deploy your Web application. If you modify a source file, ASP.NET automatically recompiles the file and updates all linked resources. The IIS server does not have to be restarted for the changes to take effect unless the <processModel> section has been changed.

You can extend the ASP.NET build system by creating custom build providers for new file types that are called during compilation.


Compile-time vs run-time

The difference between compile time and run time is an example of what pointy-headed theorists call the phase distinction. It is one of the hardest concepts to learn, especially for people without much background in programming languages. To approach this problem, I find it helpful to ask

  1. What invariants does the program satisfy?
  2. What can go wrong in this phase?
  3. If the phase succeeds, what are the postconditions (what do we know)?
  4. What are the inputs and outputs, if any?

Compile time

  1. The program need not satisfy any invariants. In fact, it needn’t be a well-formed program at all. You could feed this HTML to the compiler and watch it barf…
  2. What can go wrong at compile time:
    • Syntax errors
    • Typechecking errors
    • (Rarely) compiler crashes
  3. If the compiler succeeds, what do we know?
    • The program was well formed—a meaningful program in whatever language.
    • It’s possible to start running the program. (The program might fail immediately, but at least we can try.)
  4. What are the inputs and outputs?
    • Input was the program being compiled, plus any header files, interfaces, libraries, or other voodoo that it needed to import in order to get compiled.
    • Output is hopefully assembly code or relocatable object code or even an executable program. Of if something goes wrong, output is a bunch of error messages.

Run time

  1. We know nothing about the program’s invariants—they are whatever the programmer put in. Run-time invariants are rarely enforced by the compiler alone; it needs help from the programmer.
  2. What can go wrong are run-time errors:

    • Division by zero
    • Deferencing a null pointer
    • Running out of memory

    Also there can be errors that are detected by the program itself:

    • Trying to open a file that isn’t there
    • Trying find a web page and discovering that an alleged URL is not well formed
  3. If run-time succeeds, the program finishes (or keeps going) without crashing.
  4. Inputs and outputs are entirely up to the programmer. Files, windows on the screen, network packets, jobs sent to the printer, you name it. If the program launches missiles, that’s an output, and it happens only at run time.