The idea behind output caching is that if an ASP.NET page hasn't changed, why go through the process of recompilation and execution? Why not, after the page has been run for the first time, just store the HTML it generated, and when the page is requested again, just return the HTML? Less processing on the server means more pages can be served, and those people requesting cached pages get them quicker. Of course, the issue you have is that pages are often dynamic; they contain server controls, user controls, and data, and maybe some part of the page changes with every request, meaning if it was cached, users would always see the same page they saw after the first request. A similar issue comes into play when pages contain data from a database, where you can cache the page, but what happens if the data changes? The users would see stale data. All of these problems are solved by the ASP.NET caching framework, along with SQL Server (both 2000 and 2005) to help with the data side of things. The downside of caching is that it consumes memory, since the cache is memory-based, but a Least Recently Used (LRU) algorithm is used, meaning that items in the cache that are accessed infrequently can be removed from the cache. This ensures that the cache does not consume more memory than necessary. The caching framework is flexible and has the notion of cache dependencies, where items in the cache can be dependent upon external conditions; when the conditions change, items can be removed from the cache. Items in the cache can be dependent upon:
Which method you use depends upon your requirements, but as a general rule, caching can bring huge improvements in performance and scalability. Configuring Output CachingThe simplest caching solution is output caching, where the output of the page, or user control, is cached. The page output is the HTML that is sent back to the user, hence the term "output caching." Caching page output can be easily enabled by use of the OutputCache directive at the top of the page or user control: <%@ OutputCache %> The attributes that can be added to this directive are shown in Table 6.2.
For example, consider a grid that shows categories, each of which has a link to another page to show more of the products for that category, perhaps identified with a link such as: <asp:TemplateField> <ItemTemplate> <a href='<%#Eval("CategoryID", "ViewProducts.aspx?CategoryID={0}")%>' target="_blank">View Products</a> </ItemTemplate> </asp:TemplateField> The ViewProductsl.aspx page could cache its output depending upon the CategoryID passed in, so that multiple requests for the same product would be served from the cache: <%@ Page Language = "C#" ... %> <%@ %@ OutputCache Duration = "30" VaryByParam="CategoryID" %> The first time the page is processed, the output is cached. Subsequent requests with the same CategoryID would be served from the cache, but a different CategoryID would result in the page being rerun and the output also being cached. Now, there would be two items in the cache, and more would be added as different categories were viewed. You can easily test caching by adding a date and time at the top of the page, perhaps with the following line of code: <% =DateTime.Now %> Since this would only be executed once, when the page is first processed, subsequent requests for the page (such as simply refreshing the browser) would show the same time. If you wait until the duration is up, the page will be evicted from the cache, and the next request will reprocess the page, resulting in a new date and time. One important point to note is that the more items you have in the cache, the more memory your Web server uses and the less memory is available for dynamic use, such as for surges in requests. While caching can improve performance, you have to balance that performance with the additional resources it requires. Caching Portions of a PageThe problem with caching is that the entire contents of the page are cached. There are, however, circumstances when you would like to only cache portions of a page, and there are two ways in which you can achieve this. You can use Control caching, or fragment caching, as it is sometimes called, to cache portions of the page, or you can use post-cache substitution to cache the entire page but have portions of it dynamic upon each request. Control CachingYou implement control caching by placing the portions of the page you wish to be cached into user controls. This allows you to help with the balance between caching and resource usage, because those parts of the page that are intensive to generate, such as data-bound grids or Web Servicebased data, can be cached, while the remaining server controls and HTML can be generated each time. You exploit user controls for this by simply removing those portions of a page that you wish to cache and placing them into a user control. You can then add the OutputCache attribute to the user control, as shown in Listing 6.6. Listing 6.6. A Cached User Control
Although there is no real content in this user control, it works as a simple case to show caching. The control has the OutputCache directive, and the content is simply some text with the current date and time. Consider another user control, shown in Listing 6.7, which isn't cached, but also shows the current date and time. Listing 6.7. A Non-Cached User Control
For these to work, the user controls simply need to be included on a page, as shown in Listing 6.8. The result of the control caching is shown in Figure 6.1. Figure 6.1. Control caching in action
You can see that the date on the page heading is the same as for the noncached control, while the cached user control shows a different datethe date the page was first requested. Refresh was pressed a few times, showing that the control is cached, but not the rest of the contents. In the examples just presented, the page and control cache durations were the same, but there is no requirement for this to be the case; different values are perfectly acceptable depending upon your caching requirements. Listing 6.8. Using Cached and Non-Cached User Controls
Post-Cache SubstitutionFor the opposite situation, you can use post-cache substitution, where most of the page is cached but some portions aren't. A good example of this is advertisements, where each page request should show a new advertisement, even if the rest of the page is cached. This is in fact what the AdRotator control does. Post-cache substitution is designed for use at the control level, where the control decides that its content should not be cached. It works like this: The control implements a dynamic rendering function in a callback that is registered with the response. The cached response keeps a marker to the content, which is replaced with your real content once the item is fetched from the cache and before it is sent to the client. This type of control caching is outside the scope of general caching techniques; for more information, see Nikhil Kothari's Weblog at http://www.nikhil.net/ -. Nikhil is a member of the ASP.NET team and has written a helper class that control developers can use to aid in post-cache substitution. Disk CachingWe've mentioned that caching is always a compromise between speed of pages returned and resource usage of the server. Another form of caching is to cache the page output to disk, which reduces the memory overhead on the server. The trade-off here is that you don't have to go through the page regeneration, but you do still have file access. Disk-based caching is not built in to ASP.NET 2.0, but there is a solution written by one of the members of the ASP.NET team that can improve performance if your request sizes are large. With memory-based caching, large requests would consume lots of memory, so as request size increases, disk caching becomes more viable. Another great reason for disk caching is that the cache persists across restarts, which can reduce the time for pages to initially load. Read more about disk caching on Dmitry's Weblog at http://blogs.msdn.com/dmitryr. Configuring CachingAs well as configuring caching at the page level, you can also configure it globally in web.config, as shown in Listing 6.9, which details three sections. There is also a fourth section, SqlCacheDependency, which is covered in the section Configuring SQL Server 2000 Cache Invalidation in ASP.NET. Listing 6.9. Cache Configuration
The details for each of the elements and attributes are shown in Table 6.3, Table 6.4, and Table 6.5 respectively.
The cache section allows configuration of memory limits for caching, while the outputCache section allows configuration of output cache settings. Cache profiles are useful if you have common caching configurations that are used in multiple pages. To avoid having to modify every page with the same settings, you can configure a cache profile and use the CacheProfile attribute of the OutputCache page directive. |