Introduction
One of the most sure-fire ways to improve a web application's performance is to employ caching. Caching takes some expensive operation and stores its results in a quickly accessible location. ASP.NET version 1.0 introduced two flavors of caching:

  • Output Caching - caches the entire rendered markup of an ASP.NET web page or User Control for a specified duration.
  • Data Caching - a programmatically-accessible, in-memory data cache for storing objects in the web server's memory.
For a more in-depth discussion on ASP.NET 1.x's caching capabilities, refer to Scott McFarland's Caching with ASP.NET and Steve Smith's ASP.NET Caching: Techniques and Best Practices articles.

In ASP.NET 2.0, the caching system has been extended to include SQL cache dependenciescache profiles, and post-cache substitution for output cached pages. TheCaching for Performance section of the ASP.NET 2.0 QuickStarts provides a good overview of ASP.NET 2.0's caching options. This article explores output caching in ASP.NET 2.0, starting with an overview of output caching and followed by a detailed look at creating pages that include both cached and non-cached markup using fragment caching and post-cache substitution techniques. Read on to learn more!

An Overview of Output Caching
Output caching improves the performance of an ASP.NET application by caching the rendered markup of an ASP.NET web page and serving this content instead of re-rendering the page. For example, imagine that you had an ASP.NET page that displayed the records from a database table named Employees that listed the current employee information. Without caching, each time this page is accessed a connection is made to the database, the table queried, and the results returned to the requesting client. But how often does the employee information change? Probably not more than once a day or so, making many of those requests to the database superfluous. With output caching, when this page is first visited the rendered HTML will be cached for a specified duration. During that time period, if a user requests this page, the cached markup will be returned, thereby saving the database access and page rendering costs.

That's output caching in a nutshell! To implement output caching, simply add the <% @OutputCache %> directive to the top of your page like so:

<%@ Page ... %>
<%@ OutputCache Duration="duration" VaryByParam="paramList" %>

duration is the number of seconds for which the rendered markup remains in the cache. paramList is a list of parameters whose values vary the cache. For example, if the page that displays employees displays just those employees in a particular department as determined by a querystring parameter, then we want to vary the cache by this parameter. To be more precise, imagine that our employee page displays a particular department's employees when visited like ShowEmployees.aspx?DepartmentID=departmentID. If the cache is not varied by any parameter then when a user visits ShowEmployees.aspx?DepartmentID=Sales, the employees in the sales department will be returned and that markup will be cached. If within that cache duration a user visits ShowEmployees.aspx?DepartmentID=IT, they will still see the cached sales staff rather than the IT staff.

To fix this, we simply need to instruct the output cache engine to vary its cache by the DepartmentID parameter:

<%@ OutputCache Duration="duration" VaryByParam="DepartmentID" %>

To vary by all parameters, use an asterisk (*); to vary by no parameters, use "None".

Output Caching Subtleties
While output caching is painfully easy to implement - just add the <%@ OutputCache %> directive - there are some subtleties that are worth noting. As discussed earlier, it's important to properly vary the cache so that the correct cached version is served based on the values of the input parameters. In addition to VaryByParam, there are additional VaryByX options that should be considered. For example, you may want to vary the cache by the browser, since antequated browsers are rendered with HTML 3.2 markup. Also if you have a multi-lingual site, you'll want to vary the output by whatever parameters you're using to determine the language to display. Refer to ASP.NET Caching: Techniques and Best Practices for details on varying the cache by HTTP headers or custom values.

Looking Into the Guts of Ouptut Caching
Output caching is implemented in ASP.NET as an HTTP Module, in particular the OutputCacheModule class in the System.Web.Caching namespace. An HTTP Module listens to the events that fire during the lifecycle of a request and can execute code when a particular events fire. The OutputCacheModule HTTP Module subscribes to the ResolveRequestCacheand UpdateRequestCache events. In the ResolveRequestCache event, the module determines if the file being requested participates in output caching and, if so, there's a cache version to send back. If there's not a cached version, the page is rendered as normal. On the way out when the UpdateRequestCache event fires, the module will cache the rendered output (if the page is setup to support output caching). The following sequence diagram oversimplifies the tasks performed by the OutputCacheModule module, but highlights the overall workflow.

 

The OutputCacheModule Workflow.

If you want a page's output cache to expire before the specified duration is reached, there are two options. You can programmatically remove the output cached content for a particular page by calling the Response.RemoveOutputCacheItem(path) method, where path is the virtual absolute path to the page. You can also associate external dependencies with the output cache, as discussed in Caching Page Output with Cache Key Dependencies. This includes the ability to tie a SQL Cache Dependencies to the output cache for a page; see the SQL Cache Invalidation section of the ASP.NET 2.0 QuickStarts for more information.

Output Caching Only Part of a Page
By default, output caching caches the entire rendered markup of a page. Oftentimes, we want to keep certain portions of the page dynamic, though. We may want to cache the expensive, non-changing items - displays of data, for instance - but keep advertisements, user-specific data, or other oft-changing information "live". In ASP.NET 1.x this could only be done via fragment caching, which allows you to say, "Keep this page dynamic except for these portions." ASP.NET 2.0 supports fragment caching, but also introduces a post-cache substitution model which allows you to say the opposite: "Keep this page cached except for these dynamic portions." Let's look at fragment caching first and then turn our attention to post-cache substitutions.

Fragment caching works by creating a page without output caching that uses a User Control that does use output caching. The download at the end of this article includes a simple example - there's a User Control named CachedUserControl.ascx that displays the records from a database table along with the date/time at which the data was retrieved. This User Control contains an <% @OutputCache %> directive:

<%@ Control ... %>
<%@ OutputCache Duration="15" VaryByParam="None" %> 

... markup that displays records from a database table ...

The page that contains this User Control does not have an <% @OutputCache %> directive. The net result is that the page is re-rendered every time it's visited, but the User Control is only re-renderd (and therefore only hits the database) at most once every 15 seconds.

The following screenshot shows the page when it is first visited. Note that the date/time for the page and the User Control are the same. Revisiting the page a few seconds later, however, shows that the User Control is being cached - the page's date/time is up to the second, but the User Control time is from when it was first visited.

 

The output when the page is first visited. 

The output when the page is visited nine seconds later.

Check out the download available at the end of this article for the page and User Control used in this sample. Also, see How To Perform Fragment Caching in ASP.NET by Using Visual C# .NET for a step-by-step look at a fragment caching demo.

Making Cached Portions of a Page Dynamic Through Post-Cache Substitutions
ASP.NET 2.0 adds another technique to mix cached and dynamic content - post-cache substitutions. As the name implies, this approach takes a page that uses output caching and allows you to make "live" substitutions in the cached content. There are two ways that post-cache substitutions can be applied:

  • Declaratively - specify the dynamic portion(s) of a page using Substitution controls. You need to create a method that will be invoked by these controls in order to get their rendered markup.
  • Programmatically - the Response.WriteSubstitution(callback) method programmatically injects a response substitution block. The callback parameter is a delegate to a method that will be invoked to get the markup to emit.
To use the declarative case, simply add a Substitution control in your output cached page wherever you want to inject dynamic data.

<%@ Page ... %>
<%@ OutputCache Duration="duration" VaryByParam="paramList" %>
...
   <asp:Substitution ID="id" runat="server" MethodName="method" />
...

The MethodName(string) property specifies a static method in the page's code-behind class that must accept an HttpContext object and return the string to emit. Imagine that we had a page that included cached data, but we wanted to include a dynamic message (an advertisement, perhaps). We could add a Substitution control to the page where we wanted the message to appear:

<asp:Substitution ID="DynamicMessage" runat="server" MethodName="GetMessage" />

Here we specified the MethodName as GetMessage. Therefore, we need to create a static method in the page's code-behind class named GetMessage that returns a string:

'VB
Private Shared Function GetMessage(ByVal context As HttpContext) As String
   Return "This ad brought to you by the time " & DateTime.Now.ToString()
End Function


// C#
private static string GetMessage(HttpContext context)
{
   return "This ad brought to you by the time " + DateTime.Now.ToString();
}

When this page is visited, its content will be cached, as usual. On subsequent visits, though, when the page is still cached, the OutputCacheModule will invoke the GetMessagemethod and place its output in the appropriate sections of the page.

Conclusion
ASP.NET's output caching feature allows for a page or User Control's entire HTML output to be cached, thereby helping to boost the application's overall performance. The output caching logic is handled by an HTTP handler, OutputCacheModule. Oftentimes, a page needs to have some portions be cached, while leaving others dynamic. ASP.NET 2.0 provides two mechanisms to accomplish this - fragment caching and post-cache substitutions. With fragment caching, the page is left as a normal, dynamic page without output caching. Those regions of the page that should be cached, however, need to be implemented as User Controls that are configured to use output caching. The other avenue is to use post-cache substitution, where the page is configured to use output caching. Substitution controls can be added to the page to indicate areas where content should be dynamically generated on each page visit regardless of whether the page is cached. Post-cache substitutions are new to ASP.NET 2.0.

Happy Programming!

 

  • By Scott Mitchell

     


    Further Reading:

  • Caching for Performance
  • ASP.NET Caching: Techniques and Best Practices
  • ASP.NET Micro Caching: Benefits of a One-Second Cache
  • Implement "Donut Caching" with the ASP.NET 2.0 Output Cache Substitution Feature
  •  

    posted on 2009-11-24 23:58  shawnliu  阅读(399)  评论(0编辑  收藏  举报