Output Caching Actions Gotcha in ASP.NET MVC 3

Posted by: Malcolm Sheridan , on 3/7/2011, in Category ASP.NET MVC
Views: 71431
Abstract: The following goes through describes how to avoid the output caching problem in ASP.NET MVC 3.

This is a gotcha I had to write about. Everyone knows the benefits of caching data when working with the web. Be it file caching, donut caching or SQL Server caching, the benefits are obvious; retrieving data from memory instead of a data store means a more performance. I recently had an action that was being used frequently, so I decided to cache the action. I was trying to use a cache profile for this. I’ve used them previously in ASP.NET and the benefits of using them are incontrovertible. Moving configurable code into the web.config just makes sense.

I’ll change the real code I was using to something simple, so the action method looked like this.

And if I called that action with the following code, things worked as expected.

Now if you try to code according to better coding practices, you’d move the caching code into a cache profile. So here’s the cache profile in web.config.

Next is to update the action to use the cache profile.

Now if you run the website, you’ll encounter the Yellow Screen of Death.

Duration must be a positive number. I doubled checked the web.config to make sure I add this value, and yes I did. After much stumbling around, I peeked inside of the MVC 3 source code and looked at the OutputCacheAttribute class and found the ValidateChildActionConfiguration method.

This method is called during the OnActionExecuting event.

ValidateChildActionConfiguration throws an error if you don’t have the Duration and VaryByParam values set.

What’s the way around this? Well you can either stick to hard coding the values in the OutputCache attribute. There’s nothing wrong with that. Another option is to implement your own cache attribute. You can implement this in ASP.NET 4.0 if you create your own OutputCacheProvider object. Personally having to do this myself makes me reluctant as this is plumbing code, and I expect that it should be dealt with by the framework.

The entire source code of this article can be downloaded over here

Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles
Malcolm Sheridan is a Microsoft awarded MVP in ASP.NET, a Telerik Insider and a regular presenter at conferences and user groups throughout Australia and New Zealand. Being an ASP.NET guy, his focus is on web technologies and has been for the past 10 years. He loves working with ASP.NET MVC these days and also loves getting his hands dirty with jQuery and JavaScript. He also writes technical articles on ASP.NET for SitePoint and other various websites. Follow him on twitter @malcolmsheridan


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by Bart Czernicki on Monday, March 7, 2011 10:54 AM
Hard code values in the attribute?  You do know you can reference a static class property, right?
MyConfigClass.MyCachingTime and put that in the attribute (which can be configurable from a db, service, web.config).
Comment posted by Malcolm Sheridan on Monday, March 7, 2011 4:15 PM
@Bart
Yes I am aware of that, but having to retrieve the duration from a database or web service would degrade from what you're trying to achieve.  Also fetching the duration from the web.config goes against the DRY principal IMO.  Why add another setting when it should be read from a cache profile?

Remember I'm just writing this article to make you aware of the problem.  I'm not trying to create a complete solution.
Comment posted by Bart Czernicki on Monday, March 7, 2011 9:18 PM
Sorry, but your blog article gives VERY poor advice:
"What’s the way around this? Well you can either stick to hard coding the values in the OutputCache attribute. There’s nothing wrong with that. Another option is to implement your own cache attribute. You can implement this in ASP.NET 4.0 if you create your own OutputCacheProvider object. Personally having to do this myself makes me reluctant as this is plumbing code, and I expect that it should be dealt with by the framework."

If a developer came to me and said I have to spend 2-4 days extra writing/testing my own custom cache attributes/provides because I am breaking the DRY principle to put a config class in the attribute I would LOL him out of my office.  That value obviously could be cached :)
Like you said its plumbing code...I agree with you that it should be handled by the framework, but I would EASILY opt to put a dynamic static config class over wrting my own custom output cache provider.  If you are writing a custom one for other reasons, then you might want to add this feature.  But once again to your point...why write plumbing code?  Especially when its for something like caching duration!
Comment posted by Malcolm Sheridan on Tuesday, March 8, 2011 4:17 AM
@Bart
Agree.  Why write plumbing code when it should be in the framework?

The one point I will disagree with is fetching the cache duration from a separate store just to avoid hard coding the value.  If you need to do this, then effectively you are storing the values in locations where they shouldn't be.

I'm glad you feel so strongly about the article.  Thanks for the comments.
Comment posted by Zahir Khan on Wednesday, March 9, 2011 12:40 AM
Hey Malcom,
I did not understand this. I mean when you are mentioning varybyparam and Duration in Web.Config then why does it raise an error. Does it mean you can never use the Cache profiles set in Web.Config and have to always hard code the values in the Output Cache Attribute?
Comment posted by Imran Baloch on Friday, March 11, 2011 1:28 AM
This is not bug but a feature that make partial caching possible,

Levi(ASP.NET MVC Team Member),
"Partial caching in MVC 3 doesn't use the standard ASP.NET APIs since they're insufficient for MVC's implementation."

http://forums.asp.net/t/1640980.aspx/1
Comment posted by sankkrit on Friday, March 11, 2011 3:29 AM
How this Partial caching feature is working, as we can not specify the cache value as given in the above code. This need to be sorted out.
Comment posted by Malcolm Sheridan on Sunday, March 13, 2011 4:38 AM
@Imran
I never said this is a bug.

@Zahir
This means that the cache duration cannot be set as a cache profile.  
Comment posted by Reddy on Thursday, December 8, 2011 3:12 PM
Known Bug Using CacheProfile with Child Actions
David Hayden mentioned this in his blog post.
http://davidhayden.com/blog/dave/archive/2011/01/25/PartialPageOutputCachingASPNETMVC3.aspx
Comment posted by Pablo Romeo on Thursday, December 13, 2012 2:27 PM
I posted a pretty simple solution to this here:
http://stackoverflow.com/a/13866280/1373170
Comment posted by Jeremy on Wednesday, December 19, 2012 10:26 AM
@PabloRomeo: Nice and thanks for sharing
Comment posted by Steve Smith on Monday, February 18, 2013 6:24 PM
Fixed in the framework (better exception message; cache profiles still not supported) via this pull request: http://ardalis.com/how-to-contribute-to-aspnet-yourself
Comment posted by Thomas on Wednesday, February 20, 2013 12:32 AM
Thanks Steve Smith for that link
Comment posted by IndyItMan on Monday, December 30, 2013 7:51 AM
I am using VS2013 for an MVC4 app.  This is not fixed - I still get the "Duration" error and had to dig for over an hour to figure out the problem.
Comment posted by Jane on Monday, June 9, 2014 1:19 PM
In my case, I stopped getting the Duration not a positive number when I left out the NoStore parameter from my OutputCache declaration i.e.  [OutputCache(Duration = 10, VaryByParam = "*")].

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel