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.
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

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.
NOTEEarly 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.
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.
|
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
|
|
Please login to rate or to leave a comment.