Introduction
In this article we will see how we can implement caching in an ASP.NET MVC application.
Background
As ASP.NET web developers, we are mostly involved in developing web pages that are dynamic, i.e. contents coming from databases, Server directories,
XML files or getting pulled from some other websites. Caching means to store something in memory that is being used frequently to provide better performance.
Why should we even care about caching. Let us imagine a scenario when the contents of a web page are being pulled from a database.
The user asks for contents depending on some criteria. Now if the database is getting changed very frequently that even between two requests of same
user, we anticipate database change, then we can in no way cache the data that the user is requesting.
But if the database is not getting changed that frequently, we can have some caching in place so that if the user is requesting the same data very frequently,
we don't hit the database every time (since we know contents are not changed).
The two keys terms here are frequency and criteria. Frequency is the number of times we are anticipating the user requests for a
particular page and criteria is what governs the uniqueness of result that is being displayed on the page.
Frequency is important because we need to figure out the interval in which database is changing and compare it with the frequency of user requests
so that we can have caching in place and also make sure that user is not viewing outdated data.
Criteria is important because we need to make sure that we have caching implemented for the page on every unique criteria.
It should not be the case that user is requesting contents based on criteria01
and we are showing him the cached results of criteria00
(cached earlier for him).
So with all this theory in mind, let's go ahead and see how we can implement caching in ASP.NET MVC
Note: I have written a similar article in caching in ASP.NET Web forms. This article talks specifically about caching in ASP.NET MVC.
Since the theoretical concept behind caching is same for both the technologies, I have borrowed some of the contents from that article. That article can be
found here: A Beginner's Tutorial on Caching in ASP.NET[^]
Using the code
Types of Caching
There are two types of caching available in ASP.NET:
-
Page Output Caching
-
Application Caching
Page Output Caching
Page output caching refer to the ability of the web server to cache a certain webpage after user request in its memory so that
further requests for the same page will check for the cached page's validity and will not result in resource usage (DB access or file access) and the page will be returned to user from cache.
Let us develop a small application that will demonstrate how we can implement page output caching in ASP.NET MVC. Lets implement a simple view that will show the current time to
the user. The Controller
code is simply returning the view.
public class TestController : Controller
{
public ActionResult Index()
{
return View();
}
}
In my view, I am showing the current time to the user.
@{
ViewBag.Title = "Caching Demo";
}
Time is @DateTime.Now.ToString()
If we run this application now, it will show me the current date and time. If we refresh it, the time also gets updated(since there is no caching implemented right now).
Now lets say that this page should be cached for 30 seconds i.e. the time string should not change for 30 seconds.
We can wchieve this by adorning the action method with the OutputCache
attribute. We will pass 2 values in the attribute, First is the Duration
of cache and second
property VaryByParam
. VaryByParam="none"
specifies that caching doesn't depend on anything.
public class TestController : Controller
{
[OutputCache(Duration=30, VaryByParam="none")]
public ActionResult Index()
{
return View();
}
}
Now if we run the page and refresh it, the time will not change for 30 seconds.
In the above example, we have specified VaryByParam="none"
. Doing so can lead to a situation where user might see stale data. So if we want to invalidate the cache based on
some user selection in screen, then we can specify the VaryByParam's
value. So If I want to cache different values for page requests having different query string, I can specify the varyByParam value
as the name of that query string.(or any HTML element I want to use).
public class TestController : Controller
{
[OutputCache(Duration = 30, VaryByParam = "none")]
public ActionResult Index()
{
return View();
}
[OutputCache(Duration = 30, VaryByParam = "id")]
public ActionResult Index1()
{
return View();
}
}
So now if I run the application and navigate to Index1, I will see a cached version for each value of "id" query string.
So what we saw is that we can specify the Duration the page should be cached which is related to the talk about frequency we did earlier.
The Duration should be chosen so that during that time we are not expecting any change in data and for the criteria,
we saw how we can use VaryByParam
to make sure that the output for every different criteria is generated and the cached copy is not just presented to the user.
If we need different output pages based on any/all of the HTML element change, we can specify the list of VaryByParam ="*"
. Let us look as some other parameters that we can use to customize caching behavior.
-
VaryByParam
: List of strings that are sent to server via HTTP POST/GET that are checked to validate cache
-
VaryByCustom
: Used for custom output cache requirements
-
VaryByHeader
: HTTPs header that determines the cache validity
-
SqlDependency
: Defines the Database-tablename pair on which the validity of cache depends
Cache Location
There is one more important property of the OutputCache
attribute class which is Location
. This property will determine where will the cached data be stored on the server.
Possible values for this property can be:
-
Any
(Default): Content is cached in three locations: the web server, any proxy servers, and the web browser.
-
Client
: Content is cached on the web browser.
-
Server
: Content is cached on the web server.
-
ServerAndClient
: Content is cached on the web server and and the web browser.
-
None
: Content is not cached anywhere.
Smarter ways to specify Cache attribute
If all our action methods have same caching needs then instead of adorning all the individual action methods with this attribute, we can simply go and adorn the controller
with this attribute and the caching will be effective on all the aciton methods.
[OutputCache(Duration = 30, VaryByParam = "none")]
public class TestController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Index1()
{
return View();
}
}
In case we have multiple action methods across controllers needing the same caching behavior, we can put this caching values in the web.config
and create a cacheprofile
for it.
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="MyCacheProfile"
duration="30"
varyByParam="id"
location="Any" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
And to use these values in the action methods we just need to specify the CacheProfile
name in the action method.
[OutputCache(CacheProfile = "MyCacheProfile")]
public ActionResult Dummy()
{
return View();
}
Partial Page Caching
In asp.net Web forms, we need to create custom user controls in order to achieve partial page caching. Same can be achieved in the MVC world by creating a simple partial view.
Caching in that partial view will be governed by the OutputCache
attribute associated with the action method responsible to render the data in this partial view.
To Illustrate this let us create one more action method Index2
in the controller. This action will not cache the data. We will render a partial View in this Index2
View. The
partial view will cache the time for 10 seconds. Lets look at the action methods for these.
public ActionResult Index2()
{
return View();
}
[OutputCache(Duration = 10, VaryByParam = "none")]
public PartialViewResult PartialTest()
{
return PartialView("SamplePartial");
}
The Index2
View looks like:
@{
ViewBag.Title = "Caching Demo";
}
Time is @DateTime.Now.ToString()
<br /><br />
Partial page here: @Html.Action("PartialTest")
And finally the partial view code:
Time: @DateTime.Now.ToString()
Now if we run the application and navigate to Index2
, we can see that the time from the main page is not getting cached but the the time coming from the partial view is getting
cached for 10 seconds.
Application Caching
Application data caching is a mechanism for storing the Data objects on cache. It has nothing to do with the page caching. ASP.NET allows us to store the object in
a Key-Value based cache. We can use this to store the data that need to cached. Let us work on the same example and try to store the DateTime
string in the Application Cache
now.
Let us create one action method which will display the date on the view from the application cache.
public ActionResult Index3()
{
if (System.Web.HttpContext.Current.Cache["time"] == null)
{
System.Web.HttpContext.Current.Cache["time"] = DateTime.Now;
}
ViewBag.Time = ((DateTime)System.Web.HttpContext.Current.Cache["time"]).ToString();
return View();
}
The view corresponding to this action method is:
@{
ViewBag.Title = "Caching Demo";
}
Time is @ViewBag.Time
Now if we run the application and navigate to Index3
, we will see the time. But no matter how many times, we refresh this page, the time value will not change.
Because the value is coming from the Application cache. Also, we are not invalidating the cache at all.
There are various parameters associated with application data caching that can be used to invalidate the value and control its behavior.
-
Dependencies
: Any file or item in cache that invalidates this cache item
-
absoluteExpiration
: Absolute time when this object should be removed from cache
-
slidingExpiration
: Relative time when this object should be removed from the cache
-
priority
: Defines the priority of the item. This is useful when server runs out of memory as in that case it start removing items from cache with lowest priority first
-
onRemoveCallBack
: This is the event handler that will be called when the object is removed from cache. It gives us a place to take further actions.
Point of interest
in this article, we saw how we can implement caching in an ASP.NET application.
The sample solution contains a complete working sample for the discussed topics. Nuget packge restore is enabled for this solution so the application will download
the dependent packages before building.
This article has been written from a beginner's perspective. I hope this has been informative.
History
-
09 April 2014: First version