Improving ASP.NET Performance Part 8: Caching

In this article we’ll discuss caching. When used correctly, caching can avoid unnecessary database lookups and other expensive operations. You can also reduce latency. The ASP.NET cache is a simple, scalable, in-memory caching service provided to ASP.NET applications. It provides a time-based expiration facility, and it also tracks dependencies on external files, directories, or other cache keys. It also provides a mechanism to invoke a callback function when an item expires in the cache. The cache automatically removes items based on a least recently used (LRU) algorithm, a configured memory limit, and the CacheItemPriority enumerated value of the items in the cache. Cached data is also lost when your application or worker process recycles.

ASP.NET provides three caching techniques that we’ll summarize in the sections below.

Cache API

Use the cache API to programmatically cache application-wide data that is shared and accessed by multiple users. The cache API is also a good place for data that you need to manipulate in some way before you present the data to the user. This includes data such as strings, arrays, collections, and data sets.

A good usage example of implementing the Cache API is a product catalog. Product catalogs are good candidates because the data typically needs to be updated at specific intervals, shared across the application, and manipulated before sending the content to the client.

You should avoid using the cache API in the following circumstances:

·         The data you are caching is user-specific. Consider using session state instead.

·         The data is updated in real time.

·         Your application is already in production, and you do not want to update the code base. In this case, consider using output caching.

The cache API permits you to insert items in the cache that have a dependency upon external conditions. Cached items are automatically removed from the cache when the external conditions change. You use this feature by using a third parameter on the Cache.Insert method that accepts an instance of a CacheDependency class. The CacheDependency class has eight different constructors that support various dependency scenarios. These constructors include file-based, time-based, and priority-based dependencies, together with dependencies that are based on existing dependencies.

You can also run code before serving data from the cache. For example, you might want to serve cached data for certain customers, but for others you might want to serve data that is updated in real time. You can perform this type of logic by using the HttpCachePolicy.AddValidationCallback method.

Output Caching

The output cache enables you to cache the contents of entire pages for a specific duration of time. It enables you to cache multiple variations of the page based on query strings, headers, and userAgent strings. The output cache also enables you to determine where to cache the content, for example on a proxy, server, or a client. Like the cache API, output caching enables you to save time retrieving data. Output caching also saves time rendering content. You should enable output caching on dynamically generated pages that do not contain user-specific data in scenarios where you do not need to update the view on every request.

An example when you would want to use the output cache is reporting. Reports that contain a low number of variations are good candidates because you save time by not retrieving and processing the data each time the page is accessed.

Avoid using output caching in the following circumstances:

·         You need programmatic access to the data on your page. Consider using the cache API instead.

·         The number of page variants becomes too large.

·         The page contains a mixture of static, dynamic, and user-specific data. Consider using fragment caching instead.

·         The page contains content that must be refreshed with every view.

Partial Page or Fragment Caching

Partial page or fragment caching is a subset of output caching. It includes an additional attribute that allows you to cache a variation based on the properties of the user control (.ascx file.)

Fragment caching is implemented by using user controls in conjunction with the @OutputCache directive. Use fragment caching when caching the entire content of a page is not practical. If you have a mixture of static, dynamic, and user-specific content in your page, partition your page into separate logical regions by creating user controls. These user controls can then be cached, independent of the main page, to reduce processing time and to increase performance.

An example of a scenario that is a good candidate for fragment caching is navigation menus. Navigation menus that are not user-specific are great candidates because menus are usually rendered with each request and are often static.

You should avoid using fragment caching under the following conditions:

·         The number of page variants becomes too large.

·         The cached user controls contain content that must be refreshed with every view.

If your application uses the same user control on multiple pages, make the pages share the same instance by setting the Shared attribute of the user control @ OutputCache directive to true. This can save a significant amount of memory.

Caching Guidelines

In the sections below we’ll discuss the best practices guidelines when designing a caching strategy.

Separate Dynamic Data from Static Data in Your Pages






<tr><td colspan=3>Application Header  Welcome John Smith</td></tr>

<tr><td>Menu</td><td>Dynamic Content</td><td>Advertisments</td></tr>

<tr><td colspan=3>Application Footer</td></tr>





<%@ Register TagPrefix=”app” TagName=”header” src=”header.ascx” %>

<%@ Register TagPrefix=”app” TagName=”menu” src=”menu.ascx” %>

<%@ Register TagPrefix=”app” TagName=”advertisements”

src=”advertisements.ascx” %>

<%@ Register TagPrefix=”app” TagName=”footer” src=”footer.ascx” %>




<tr><td colspan=3><app:header runat=server /></td></tr>

<tr><td><app:menu runat=server /></td><td>Dynamic

Content</td><td><app:advertisements runat=server /></td></tr>

<tr><td colspan=3><app:footer runat=server /></td></tr>




<%@Control %>

Application Header  Welcome <% GetName() %>



<%@Control %>

<%@ OutputCache Duration=”30″ VaryByParam=”none” %>



<%@Control %>




<%@Control %>

<%@ OutputCache Duration=”60″ VaryByParam=”none” %>



Configure the Memory Limit

Configuring and tuning the memory limit is critical for the cache to perform optimally. The ASP.NET cache starts trimming the cache based on a LRU algorithm and the CacheItemPriority enumerated value assigned to the item after memory consumption is within 20 percent of the configured memory limit. If the memory limit is set too high, it is possible for the process to be recycled unexpectedly. Your application might also experience out-of-memory exceptions. If the memory limit is set too low, it could increase the amount of time spent performing garbage collections, which decreases overall performance.

Empirical testing shows that the likelihood of receiving out-of-memory exceptions increases when private bytes exceed 800 megabytes (MB). A good rule to follow when determining when to increase or decrease this number is that 800 MB is only relevant for .NET Framework 1.0. If you have .NET Framework 1.1 and if you use the /3 GB switch, you can go up to 1,800 MB.


<processModel memoryLimit=”50″>

This value controls the percentage of physical memory that the worker process is allowed to consume. The process is recycled if this value is exceeded. In the previous sample, if there are 2 gigabytes (GB) of RAM on your server, the process recycles after the total available physical RAM falls below 50 percent of the RAM; in this case 1 GB. In other words, the process recycles if the memory used by the worker process goes beyond 1 GB. You monitor the worker process memory by using the process performance counter object and the private bytes counter.

Cache the Right Data



Refresh Your Cache Appropriately


Cache the Appropriate Form of the Data


Use Output Caching to Cache Relatively Static Pages





Choose the Right Cache Location



·         Client. The output cache is located on the browser client where the request originated.


·         None. The output cache is disabled for the requested page.




Use VaryBy Attributes for Selective Caching


·         VaryByParam. Different versions of the page are stored based on the query string values.

·         VaryByHeader. Different versions of the page are stored based on the specified header values.




<%@ OutputCache Duration=”30″ VaryByParam=”a” %>


·         http://localhost/cache.aspx?a=1

·         http://localhost/cache.aspx?a=1&b=1

·         http://localhost/cache.aspx?a=1&b=2


(MAX a × MAX b) + (MAX a + MAX b) = 65 total variations
