One of the common issues of web applications is performance and serving the same content over and over again for hours, days, even months is certainly effecting the performance of our web applications. This is where server side caching comes in handy. But sometimes the whole page we are scripting on the server side is going to be static for hours and sometimes some portion of it.
The first one, caching the whole page, is sort of easy with almost any web development frameworks. The second one, caching a portion of your web page, is the tricky one.
Phil Haack blogged about Donut Hole Caching in ASP.NET MVC a while back but it is a little outdated. In this quick blog post, I will try to show you the easiest way of implementing this feature with ASP.NET MVC.
Use Cases
So, where might we need this Donut Hole Caching thing? For example, if you are listing categories on the side bar, you probably gathering those categories from a database. Add the fact that you render this part on every single page of your web application, it is a waste of time if your category list is not so dynamic. So, you may want to cache the category list part of the page so that you don’t go to your database every single time.
How to Do
In ASP.NET MVC framework, we can cache the whole controller action with OutputCacheAttribute which represents an attribute that is used to mark an action method whose output will be cached according to MSDN. This is perfect but wait a second. We do not want to cache the entire action, we just want to cache the portion of it. This is where ChildActionExtensions.Action method, which invokes a child action method and returns the result as an HTML string, plays a huge role. Let me show you how these parts of the framework play nice together.
Have a look at the below controller action :
[ChildActionOnly] [OutputCache(Duration=60)] public ActionResult sampleChildAction() { //Put the thread at sleep for 3 seconds to see the difference. System.Threading.Thread.Sleep(3000); //Also pass the date time from here just to see that it is comming from here. ViewBag.DateTime = DateTime.Now.ToString("dd.MM.yyyy HH:mm.ss"); return View(); }
A simple controller action method which returns ActionResult, nothing fancy going on except for ChildActionOnlyAttribute which represents an attribute that is used to indicate that an action method should be called only as a child action. Let’s look at the sampleChildAction view and I will try to explain ChildActionOnlyAttribute function after that.
@{ Layout = null; } <p> This portion of the web page was scripted on <strong>@ViewBag.DateTime</strong> and I will be cached for <strong>60</strong> seconds. </p>
This html will be a part of our web page which will be cached. It doesn’t mean anything by itself but we have created an action for this view which means that we can call this page directly from a browser. ChildActionOnlyAttribute exactly prevent users to call this kind of actions. You do not need to implement this attribute there but it is nice to know that it is there for us.
The controller action which will render the whole page is so simple as below and doesn’t require any special thing for us to implement in order caching to work.
public ActionResult Index() { return View(); }
Let’s also look at the view implementation :
@{ ViewBag.Title = "Donut Hole Caching Sample"; } <h2>Donut Hole Caching Sample</h2> <h3>Cached</h3> <div> @Html.Action("sampleChildAction", new { controller = "Sample", Area = "DonutHoleCaching" } ) </div> <h3>Normal</h3> <div> <p> This portion of the web page was scripted on <strong>@DateTime.Now.ToString("dd.MM.yyy HH:mm.ss")</strong> </p> </div>
What we are doing here is that rather than putting the part, which we would like to cache, directly here, we are calling it as child action. So, the framework will treat the child action as it does for normal action methods.
When we first call it, we will see something like below :
While I was calling this page, I was on hold for 3 seconds because we have put the thread at sleep for 3 seconds on our child action method to feel the difference as you can see on above code in order.
When I make the second call, I got something like this and I wasn’t on hold for 3 seconds :
Did you notice the time difference? This proves that if the cache is valid, our child action method won’t be rendered again. It will serve from the cache. Awesome, ha?
I decided to create an ASP.NET MVC project called MvcMiracleWorker for this kind of small samples. You can find the complete source code from GitHub : https://github.com/tugberkugurlu/MvcMiracleWorker
Behave well, use ASP.NET MVC