Published: 22 Dec 2010
By: Xianzhong Zhu
Download Sample Code

In this series of articles, we are going to explore as many as possible aspects of cache programming in the latest ASP.NET MVC 3 RC2 framework. And also, all the related samples have been tested against the latest ASP.NET MVC 3 RC 2.

Contents [hide]

The Experience ASP.NET MVC 3 Beta series

  • Part 1 In the coming ASP.NET MVC 3.0 a lot of new good things will be added or enhanced. In this article, we are going to focus upon the new view engine Razor to see how it will simplify the view design.
  • Part 2 In addition to the introduction of a new view engine Razor, ASP.NET MVC 3 Beta has also introduced numerous new HtmlHelpers, such as Chart, Crypto, WebGrid, WebImage, WebMail, etc. This article aims to introduce the two commonly-used new helper controls – WebGrid and Chart using relevant examples.
  • Part 3 In this installment, I'll first tell you a short story about unobtrusive JavaScript. Then, we'll delve into the unobtrusive client-side validation support. Finally, we will research into a more interesting story - the unobtrusive jQuery-Based Ajax support.
  • Part 4 In this article, we are going to continue to explore the other three important helpers - WebImage, WebMail, and Crypto.
  • Part 5 Starting from this article, let's explore some more advanced concepts and related utilizations associated with flexible Dependency Injection support introduced in ASP.NET MVC 3 Beta.
  • Part 6 In the last article, we've mainly discussed the new-styled DI support in ASP.NET MVC 3 Beta/RC in relation to the two new services - IControllerActivator and IViewPageActivator. Obviously, both of them are connected with controllers and views. In this article, however, we will shift our attention to the Model (generally called viewmodal in many blogs) related DI manipulations.
  • Part 7 Since ASP.NET MVC 1, CSRF (Cross Site Request Forgery) has been considered by introducing a set of anti-forgery helpers. In this article, we are to detail into CSRF related concepts and ASP.NET MVC's helper functions again CSRF.
  • Part 8 In this series of articles, we are going to explore as many as possible aspects of cache programming in the latest ASP.NET MVC 3 RC2 framework. And also, all the related samples have been tested against the latest ASP.NET MVC 3 RC 2.
  • Part 9 In the first part of this series, we've mainly explored Output Cache related issues. In this second part, however, we are going to delve into the general Data Cache topic.
  • Introduction

    As is well know, ASP.NET platform provides two major caching tools: one is Data Caching via the HttpContext's Cache object, through which you can cache arbitrary .NET objects; the other is Output Caching, which records the HTML response sent by an action method, and replays it for subsequent requests to the same URL, reducing the number of times that your action method code actually runs. With the underground architecture based on ASP.NET, the ASP.NET MVC framework does not change much in terms of buffering techniques. Despite this, there are still a couple of details worthy of your great attention.

    In this series of articles, we are going to explore as many as possible aspects of cache programming in the latest ASP.NET MVC 3 RC2 framework. And also, all the related samples have been tested against the latest ASP.NET MVC 3 RC 2.

    In this article, we are going to focus upon Output Caching related issues and corresponding solutions in ASP.NET MVC 3. In this subsequent article, however, we will turn to explore the general Data Caching related stories.

    NOTE

    The test environments we'll utilize in the sample application are:

    1. Windows XP Professional (SP3);

    2. .NET 4.0;

    3. Visual Studio 2010;

    5. SQL Server 2000/2005/2008 and the related sample database Northwind;

    5. ASP.NET MVC 3 RC 2 (http://www.microsoft.com/downloads/en/details.aspx? FamilyID=955d593e-cbd1-4ed1-88eb-02ff79dd74d8&displaylang=en). If you've installed MVC 3 RC1, then the RC2 installment will automatically override RC1.

    Introduction

    Output caching plays an important role in terms of modern Web system performance. Under many cases, it is acceptable for a Web page response to be a little stale if this brings significant performance advantages. Let's take an e-commerce application and its set of pages for the products catalog for example. It is usually expensive to create these Web pages because they often require one or more database invocations or even more complex forms of data join. As a result, a page like this could easily cost you a few million CPU time. Why should you regenerate this same page again and again? Product pages tend to remain the same for weeks and are rarely updated more than once per day. Whatever the refresh rate is for the pages, there's little value in regenerating them on a per-request basis.

    A much better strategy is to create the page once, cache it somewhere, and give the page output a proper period of duration. Once the cached page becomes stale, the first incoming request will be served in the standard way, running the page's code, and the new page output will be cached for another period until it also becomes stale.

    As you've imaged, ASP.NET page output caching comes to help. It helps you to cache page responses, so that following requests can be satisfied without executing the page one more - instead by simply returning the cached output.

    On the whole, output caching can be supported at two levels: one is an entire page; the other is portions of a page (which gets better supported in the latest ASP.NET MVC version). Page caching is smart enough to let you save distinct output based on the requesting URL, query string or form parameters, and even custom strings.

    Since MVC's caching mechanism is built upon the infrastructure of ASP.NET cache support, there are quite some resemblance between them. Next, let's look at how ASP.NET MVC provides output caching support via the OutputCache filter.

    Detailing into the OutputCache Filter

    As with core ASP.NET's output-caching feature, ASP.NET MVC introduces an OutputCacheAttribute filter that lets you specify a set of parameters to describe when to vary the action's output. This obviously results in a trade-off between flexibility and performance.

    OutputCacheAttribute tells ASP.NET to cache the action method's output so that the same output will be reused next time the action method is requested, as a result of which the server side throughput can be greatly increased. As for subsequent requests, it eliminates almost all the expensive parts of request processing (such as database queries). Of course, the cost of this is that you're limited to producing the exact same output in response to each such request.

    To gain a better understanding with the OutputCache filter, let's take a look at the main members contained in the class OutputCacheAttribute.

    Many of the above properties play a significant role in output buffering when used together with the OutputCache filter. The following abstracts the role of each of the above properties:

    • Duration (int, mandatory): used to specify how long (in seconds) the output remains cached.
    • VaryByParam (string, mandatory in MVC 2.0 and optional in MVC RC2): used to tell ASP.NET whether to use a different cache entry for each combination of Request.QueryString and Request.Form values matching these names. In detail, this property corresponds to a semicolon- separated list of strings used to vary the output cache. By default, these strings correspond to a query string value sent with GET method attributes, or a parameter sent using the POST method. When this attribute is set to multiple parameters, the output cache contains a different version of the requested document for each combination of specified parameters. Possible values include none (meaning "Don't vary by query string or form values"), an asterisk (*, meaning "Vary by all query string and form values"), and any valid query string or POST parameter name. If unspecified, it takes the default value none.
    • VaryByHeader (string, optional): Tells ASP.NET to use a different cache entry for each combination of values sent in these HTTP header names.
    • VaryByCustom (string, optional): If specified, ASP.NET calls the Global.asax.cs file's GetVaryByCustomString() method passing this arbitrary string value as a parameter, so you can generate your own cache key. The special value browser is used to vary the cache by the browser's name and major version data.
    • VaryByContentEncoding (string, optional): Allows ASP.NET to create a separate cache entry for each content encoding (e.g., gzip, deflate) that may be requested by a browser.
    • Location (of type OutputCacheLocation, optional): Specifies where the output is to be cached. This parameter takes one of the following enumeration values: Server (in the server's memory only), Client (by the visitor's browser only), Downstream (by the visitor's browser, or by any intermediate HTTPcaching device, such as a proxy server), ServerAndClient (combination of Server and Client), Any (combination of Server and Downstream), or None (no caching). If not specified, it takes the default value Any.
    • NoStore (bool, optional): If true, tells ASP.NET to send a Cache-Control: nostore header to the browser, instructing the browser not to store (i.e., cache) the page for any longer than necessary to display it. If the visitor later returns to the page by clicking the back button, this means that the browser needs to resend the request, so there is a performance cost. This is only used to protect very private data.
    • CacheProfile (string, optional): If specified, instructs ASP.NET to take cache settings from a particular named <outputCacheSettings> node in Web.config.
    • SqlDependency (string, optional): under the case that you specify a database and table name pair, this will result in the cached data to expire automatically when the underlying database data changes. Before this will work, you must also configure the core ASP.NET SQL Cache Dependency feature.
    • Order (int, optional): The Order property takes an integer value that must be 0 (the default) or greater, with one exception. Omitting the Order property gives the filter an order value of -1, which indicates an unspecified order. Any action filter in a scope whose Order property is set to -1 will be executed in an undetermined order, but before the filters that have a specified order.

    If you have been familiar with ASP.NET's output-caching facility before, you are sure to recognize these options. In fact, OutputCacheAttribute is really just a wrapper around the core ASP.NET output-caching facility. For that reason, it always varies the cache entry according to URL path. If you have parameters in your URL pattern, then each combination of parameter values forces a different cache entry.

    Put Output Caching into Action

    Starting from this section, we are going to create concrete samples to illustrate how to use output caching to improve the performance of the system.

    The simplest case

    Enabling output caching is simple; what only needs you do is typically just to add an [OutputCache] attribute to either an individual controller action or an entire controller class.

    Let's start with a simplest case with OutputCache. Listing 2 illustrates a cached action named OutputCacheCase1. The output of this action will be cached for 60 seconds.

    In reality, if you prefer, you can specify a much longer cache duration time. For example, if you want to cache the output of a controller action for one hour. Then you can specify a cache duration of 3600 seconds (60 seconds * 60 minutes).

    Note, however, there is no guarantee that content will be cached for the amount of time that you specify. When memory resources become low, the cache content will get evicted automatically.

    Now, let's look at the related view named OutputCacheCase1 in Listing 3 below.

    The view simply outputs the current time, as shown in Figure 1.

    Figure 1: Simplest case of a cached view

    Simplest case of a cached view

    Now, if you invoke the OutputCacheCase1() action multiple times by entering the URL "http://localhost:yourportnumber/Home/OutputCacheCase1" in the address bar of your browser and hitting the Refresh button in your browser repeatedly, then you will notice that the time displayed by this view won't change for 60 seconds - the same time is displayed! Why? This is because the view is cached.

    Do note that the same view is cached for everyone visiting the page. In another word, anyone invoking the OutputCacheCase1() action will catch sight of the same cached version of the OutputCacheCase1 view. Hence, the amount of work that the web server must perform to serve the OutputCacheCase1 view will be dramatically reduced.

    Till now, the preceding case is really simple. However, you could just as easily cache a view that displays a set of database records. In that case, the set of database records will not need to be retrieved from the database each and every time the controller action that returns the view is invoked. As a result, the amount of work that both your web server and database server must perform can be reduced via Caching.

    In practice, using OutputCache filter together with the two properties Duration and VaryByParam can handle most situations. For example, if your product directory allows users to view the category pages based on the two variables categoryID and page, you can use the following implementation:

    This will create a separate cache entry for each item in each category. Each entry, counting from the first request, will be maintained for 60 seconds.

    NOTE:

    In the latest ASP.NET MVC 3 RC2, you are no longer required to specify a VaryByParam property when declaring an [OutputCache] attribute on a Controller action method. MVC 3 can vary the output cached entries automatically for you when you have explicit parameters on the action method.

    Where content is cached

    By default, when we use the OutputCache filter, content is cached in the following three locations:

    • the web server
    • any proxy server
    • the web browser

    In fact, we can specify exactly where content is cached by modifying the Location property of the [OutputCache] attribute. In detail, we can set the Location property to one of the following values:

    • Any
    • Client
    • Downstream
    • Server
    • None
    • ServerAndClient

    By default, the Location property has the value Any. However, there are situations in which you might want to cache only on the browser or only on the server. For example, if we are caching information that is personalized for each user then we should not cache the information on the server. If we are displaying different information to different users then we should cache the information only on the client.

    Using the Authorize filter carefully together with OutputCache

    In ASP.NET MVC, filters are a group of special .NET attributes that add extra steps to the request processing pipeline. To restrict access to an ASP.NET MVC view, you can restrict access to the action method that renders the view. To do this, the framework introduces an AuthorizeAttribute class. As you can image, the Authorize attribute will run first before running any other filters or the action method.

    So, problem arises: what if we combine an authorization filter with the OutputCache filter? In this case, we are possible to run the risk of an authorized user first visiting the action, causing it to run and be cached, shortly followed by an unauthorized user, who gets the cached output even though they aren't authorized.

    Fortunately, Microsoft has anticipated this –adding a special logic to AuthorizeAttribute to make it go well with ASP.NET output caching. For this, when you plan to create your own authorization filter you are highly suggested to inherit from AuthorizeAttribute rather than start from scratch - deriving from FilterAttribute and implementing the interface IAuthorizationFilter; otherwise, you'll risk allowing unauthorized users to obtain cached content.

    Using a cache profile

    To create a uniform solution in real scenarios, it is a better choice to configure output cache properties by creating a cache profile in the web configuration (web.config) file. On the whole, creating a cache profile in the web configuration file bears the following advantages:

    • By creating one cache profile, we can apply it to any specified controllers or related actions to avoid code duplication.
    • We can easily modify the web configuration file without recompiling the application. This is particularly useful when the system has already been deployed and needs further redeployment.

    For example, the <caching> web configuration section below defines a cache profile named OneMinute.

    Remember that the <caching> section must be located within the <system.web> section of a web configuration file.

    With the above definition, the controller in Listing 6 illustrates how you can apply the OneMinute profile to an action with the [OutputCache] attribute.

    If you invoke the ProfileTest() action exposed by the controller in Listing x, then the same time will be returned for 1 minute.

    Using SqlDependency together with OutputCache

    As is stressed previously, SqlDependency in an important property associated with database and table buffering. But to leverage this feature will revolve around the core ASP.NET SQL Cache Dependency feature.

    Below gives the key points to use the SqlDependency property together with the OutputCache filter in the ASP.NET MVC 3.0 environment.

    1. In the file Web.config, add the database connection string, as follows:

    2. Add the cache configuration in the file Web.config, as follows:

    3. In the global file Global.asax.cs, enable cache dependencies programmatically, as follows:

    4. Use the command line to register cache dependencies, as follows:

    5. Use the SqlDependency property together with the OutputCache filter in the action method:

    There are several points worth noticing here. First, for simplicity, I've used the built-in SQL Server 2005 Expression. Second, I've not given the detailed related database and table manipulations. Third, between SQL Server 2000, 2005, and 2008 there are quite a few differences in terms of caching support. So, the example given here is just for your reference. If possible, I'll later on write an independent article particularly researching into this problem.

    For more details about the SqlDependency property, please refer to the related materials in MSDN.

    Using VaryByHeader and VaryByCustom together with OutputCache

    The VaryByHeader property is used to tell ASP.NET to use a different cache entry for each combination of values sent in the HTTP header names.

    In real cases, VaryByHeader and VaryByCustom are mainly used to customize the appearance or content of the page accessed by the client. For instance, one URL may need to provide rendering output for both browser and mobile phone client simultaneously. Under such a circumstance, we need to provide different caching content versions for different client sides. Another example is the page may have been optimized for IE, but need to be reduced for the full optimization of Netscape or Opera.

    The later case is commonly-used. Let's consider a related example, as shown below.

    For a more detailed example around VaryByCustom, you can refer to Karsten Januszewski's blog "OutputCache in ASP.NET MVC To Support Caching For Logged-in Users".

    Partial Output Caching get supported in ASP.NET MVC 3

    In addition to supporting full page output caching, ASP.NET MVC 3 (in RC1 and RC2) has also provided supports for partial-page caching – which allows you to cache a part of output and re-use it across multiple requests or controllers.

    Moreover, the OutputCache feature for partial-page caching has been updated in ASP.NET MVC RC2, so that sub-content cached entries are varied based on input parameters as opposed to the URL structure of the top-level request, which makes caching scenarios both easier and more powerful than that in the previous version.

    NOTE

    Early versions of MVC framework do not support partial output caching directly. To achieve the partial-page caching functionality in previous ASP.NET MVC 1.0 or 2.0, you can refer to Steve Sanderson's great blog here.

    For a detailed example concerning partial-page buffering in the ASP.NET MVC 3 framework, you can refer to Scott Guthrie's article "Announcing the ASP.NET MVC 3 Release Candidate" at aspalliance.com. So, herein we'll no more illustrate related examples.

    No More Response.WriteSubstitution

    As early as ASP.NET MVC 1, there was a well-noticed feature called post-cache substitution. With this support, we could easily display dynamic content in a page - for example, you wanted to display a banner advertisement in the page. With the help of post-cache substitution, you could able to substitute dynamic content in a page that has been cached in memory. Using post-cache substitution requires two steps. First, you need to define a method that returns a string that represents the dynamic content that you want to display in the cached page. Next, you call the HttpResponse.WriteSubstitution() method to inject the dynamic content into the page.

    Regrettably, since ASP.NET MVC 2.0, the Response.WriteSubstitution method doesn't get supported any more. According to the ASP.NET Team's reply, one workaround for this situation is to do caching within the controller and always let the entire view re-render. Another alternative is to use AJAX calls from the web browser to update regions of the page that should not be cached.

    Moreover, what we've covered above is also called donut-caching or cache substitution. For this related story, Phil Haack wrote a blog in 2008 explained it here. But later, at the UPDATE part at the very beginning, he added "This technique is NOT RECOMMENDED for ASP.NET MVC 2".

    Story is not ended

    In real scenarios, things are usually more complex than imaged theoretically. For example, when you use impose the OutputCache filter upon the target actions together with another attribute AntiForgeryToken (which has been discussed in our earlier articles at dotnetslackers), trouble comes. For more details and suggested solution, please refer to Diego Marcet's blog titled "Working together with AntiForgeryToken and OutputCache on ASP.NET MVC". For brevity, we are also not to detail it any more.

    What's more, by digging further, you will find out that the implementation of output caching varies with the ASP.NET process model in use. To continue to explore this issue, I suggest referring to chapter 14 "ASP.NET Caching" in Dino Esposito's famous works "Programming ASP.NET 2.0 Core Reference". In this book Dino detailed into IIS 5.0 and 6.0 process models especially in terms of their kernel caching implementation.

    In addition, you are also recommended to read Scott Guthrie's blog "Extensible Output Caching with ASP.NET 4 (VS 2010 and .NET 4.0 Series)” to gain a better idea of the latest improvement in the underlining infrastructure.

    Summary

    Taking advantage of output caching, we can dramatically improve the performance of ASP.NET MVC applications. Instead of regenerating a page each and every time the page is requested, the page can be generated once and cached in memory for multiple users. Hence, output caching is a great technique for you to have to spend energy upon in order to improve your MVC system performance.

    In this article, we first introduced the OutputCache Filter related properties. Then, we delved into some important properties via scraps of examples. As you've seen, any buffering functionality in MVC is closely relevant to the underlining caching architecture in ASP.NET. And also, the latest partial-page buffering gets drastically ameliorated (owing to the great improvement in ASP.NET 4.0). So, to gain an ideal understanding with the caching techniques in ASP.NET MVC you have to grasp the buffering mechanism in ASP.NET first. In the next article, we are going to explore the general Data Caching related topics in ASP.NET MVC 3.

    The Experience ASP.NET MVC 3 Beta series

  • Part 1 In the coming ASP.NET MVC 3.0 a lot of new good things will be added or enhanced. In this article, we are going to focus upon the new view engine Razor to see how it will simplify the view design.
  • Part 2 In addition to the introduction of a new view engine Razor, ASP.NET MVC 3 Beta has also introduced numerous new HtmlHelpers, such as Chart, Crypto, WebGrid, WebImage, WebMail, etc. This article aims to introduce the two commonly-used new helper controls – WebGrid and Chart using relevant examples.
  • Part 3 In this installment, I'll first tell you a short story about unobtrusive JavaScript. Then, we'll delve into the unobtrusive client-side validation support. Finally, we will research into a more interesting story - the unobtrusive jQuery-Based Ajax support.
  • Part 4 In this article, we are going to continue to explore the other three important helpers - WebImage, WebMail, and Crypto.
  • Part 5 Starting from this article, let's explore some more advanced concepts and related utilizations associated with flexible Dependency Injection support introduced in ASP.NET MVC 3 Beta.
  • Part 6 In the last article, we've mainly discussed the new-styled DI support in ASP.NET MVC 3 Beta/RC in relation to the two new services - IControllerActivator and IViewPageActivator. Obviously, both of them are connected with controllers and views. In this article, however, we will shift our attention to the Model (generally called viewmodal in many blogs) related DI manipulations.
  • Part 7 Since ASP.NET MVC 1, CSRF (Cross Site Request Forgery) has been considered by introducing a set of anti-forgery helpers. In this article, we are to detail into CSRF related concepts and ASP.NET MVC's helper functions again CSRF.
  • Part 8 In this series of articles, we are going to explore as many as possible aspects of cache programming in the latest ASP.NET MVC 3 RC2 framework. And also, all the related samples have been tested against the latest ASP.NET MVC 3 RC 2.
  • Part 9 In the first part of this series, we've mainly explored Output Cache related issues. In this second part, however, we are going to delve into the general Data Cache topic.
  • <<  Previous Article Continue reading and see our next or previous articles Next Article >>

    About Xianzhong Zhu

    I'm a college teacher and also a freelance developer and writer from WeiFang China, with more than fourteen years of experience in design, and development of various kinds of products and applications on Windows platform. My expertise is in Visual C++/Basic/C#, SQL Server 2000/2005/2008, PHP+MyS...

    This author has published 81 articles on DotNetSlackers. View other articles or the complete profile here.

    Other articles in this category


    Code First Approach using Entity Framework 4.1, Inversion of Control, Unity Framework, Repository and Unit of Work Patterns, and MVC3 Razor View
    A detailed introduction about the code first approach using Entity Framework 4.1, Inversion of Contr...
    jQuery Mobile ListView
    In this article, we're going to look at what JQuery Mobile uses to represent lists, and how capable ...
    JQuery Mobile Widgets Overview
    An overview of widgets in jQuery Mobile.
    Exception Handling and .Net (A practical approach)
    Error Handling has always been crucial for an application in a number of ways. It may affect the exe...
    jQuery Mobile Pages
    Brian Mains explains how to create pages with the jQuery Mobile framework.

    You might also be interested in the following related blog posts


    Get Ready for Teleriks Custom-built Extensions for ASP.NET MVC read more
    MvcContrib working on Portable Areas read more
    GiveCamps Get a new Sponsor read more
    Announcing the Microsoft AJAX CDN read more
    Scenarios for WS-Passive and OpenID read more
    More On The CodePlex Foundation read more
    MvcContrib version control has moved to GitHub read more
    Application Request Routing (ARR) Version 2 for IIS7 Beta 2 released. read more
    Afternoon of ASP.NET MVC [16 June] (free Headspring event) read more
    ASP.NET MVC and the templated partial view (death to ASCX) read more
    Top
     
     
     

    Please login to rate or to leave a comment.

    Free Agile Project Management Tool from Telerik
    TeamPulse Community Edition helps your team effectively capture requirements, manage project plans, assign and track work, and most importantly, be continually connected with each other.