Caching an ASP.Net MVC 5.0 application

In this post I am going to provide you with a hands on example on using caching in an ASP.Net MVC 5.0 application using Visual Studio 2015 and C#.

More particularly I'll show you how to use the underlying ASP.net caching engine to improve the performance of an application. 

There is a number of different cache settings you can use.These settings are all available from the underlying ASP.net cache engine. It's the same engine that Web Forms uses. It's not something that's specific to just the MVC framework.

But first things first. What is caching why do we need caching in the first place?

Caching is of vital significance in any high-performance web application.

Caching provides a way of storing frequently accessed data and reusing that data.

It is an effective way for improving web application’s performance and user experience.

The main advantages of caching include:

  • Reduce network traffic - Content is cached at the client side, thus reducing network traffic
  • Reduce database server round trips -Content that is cached at the web server, reduces requests to the database
  • Reduce hosting server round-trips - Requests to the server are reduced
  • Not generating reusable content - Avoid time and resource consumption for regenerating reusable content


It is profound that caching benefits the performance and user experience for a web application.

The following points indicate the situation when caching should be considered

  • Content that is frequently accessed should be a good candidate for caching
  • Content thatis unique for a user/session should not be cached
  • Content that is very infrequently accesses should not be cached
  • For content that is dynamically produced and changes frquently do not disable caching. Do define a shorter cache–expiration instead.
  • When you want to cache versions of a page based on request such as cookies, theme, browser ,you could use the VaryByCustom function


When it comes to Caching an ASP.Net MVC 5.0 application, we will use Output caching.

Output caching allows you to store the output of a particular controller action in memory.

Then the ASP.net engine can respond to future requests for the same action just by giving back the cached result.

By doing that no code inside the action is executed. Having said that even though ASP.Net MVC is an entirely new paradigm on building an ASP.Net web application, is based on ASP.Net infrastructure and on the caching functionality that ship with ASP.Net.

In the ASP.Net MVC world we are familiar with terms like Model, View, Controller, ActionResults, Action Selectors, Code Expressions, Razor expressions and Action Filters. Using Action Filters is the way to enable Output Caching in a controller action.

By default, this attribute filter caches the data for 60 seconds. After 60 seconds, you guessed it, a call is made again to this action and ASP.NET MVC will cache the output again.

You can cache controller actions that produce any king of result (Views, static contents, Json). 

I pointed out before we should cache content that is frquently accessed so in our MVC application we will cache these actions in a controller that are called often and execute "expensive statements" like db queries.

Do not randomly cache any action method in a controller. You must devise a successful caching strategy and to really know where your traffic is going. You have to know what are the most expensive operations in your software.

You have to take some measurements and do some logging in order for caching to really work effectively.

Let's move on to the actual hands-on example.

If you are not familiar with ASP.Net MVC please have a look at this post of mine.

1) I have installed Visual Studio 2015 Enterprise edition in my machine which is Windows 8 by the way.I have also installed SQL Server 2014 Express Edition in my machine.

I am going to build an ASP.Net MVC 5.0 application using Entity Framework and Code First. We will target the 4.5.2 version of the framework. Firstly I will start with a simple example with no database access.

ASP.Net MVC does not dictate what kind of data access architecture we will use in our application. It does not also dictate how to build our business layer (domain classes and objects).

2) I am launching VS 2015 and I will Visual C# as the programming language. I will also select ASP.NET MVC 5 Web Application from the available templates (Empty Template using MVC).

3) I am adding a HomeController.cs class in the Controllers folder.

4) The code in the Controller action follows

public class HomeController : Controller
{
// GET: Home

[OutputCache(Duration =20, VaryByParam="none")]
public ActionResult Index()
{

ViewBag.Message = DateTime.Now.ToString();
return View();
}
}

5) Set a break point at the beginning of this action and then run with the debugger.

6) What you should see here is on this first request, we will hit that break point so we're inside of the index action and you'll notice the one parameter I have to specify with the output cache attribute is the duration.

7) How long do I cache the response? It's specified in seconds and this is 20 seconds. So now if I press F5 to continue with the debugger, I can now see the home page and the caching time and now I can refresh this as many times as I want and the caching logic inside of ASP.net is looking at the request, seeing that there's a cached response for this request.

8) The code inside the Index.cshtml view follows:


@{
ViewBag.Title = "Index";
}

<h2>Index</h2>

<div>
<b>Caching Time:</b>@ViewBag.Message

</div>

9) Remove the debugger from the Action and then run the application again. Refresh the page.For a period of 20 seconds you will see the same time. After 20 seconds the action method will run again and cache the output again for another 20 seconds.

Let's have a look now in an example where we cache results from a database.

10)I will create a new ASP.Net MVC 5  application using the Empty Template (MVC). Now I will add a model to the application.I will use this model to manage footballer's data.I will use Entity Framework as the data access technology. More specifically I will use the Code First paradigm. You might have come across the term POCO classes.This is exactly what I am going to use.If you want to learn more about Code First have a look in this post

Then from the model class we will create our database.In the Solution Explorer right-click on the Models folder,select Add and then select Class

Name the class Footballer.cs and add some properties to the class.My complete class follows

    public class Footballer
    {
        public int FootballerID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public double Weight { get; set; }
        public double Height { get; set; }
        public DateTime JoinedClub { get; set; }
     

    }

Let me explain what I am doing here. I will use the Footballer class to represent footballers in a database (which I am going to create). Each instance of a Footballer class will correspond to a row within a database table.Naturally  each property of the Footballer class will map to a column in the table (which I am going to create).

We need to add some more code in another calss to the FootballerDBContext.cs

    public class FootballerDBContext : DbContext
    {
        public DbSet<Footballer> Footballers { get; set; }
    }

The FootballerDBContext is a database context class.This class is responsible for talking to the underlying database,storing and updating the data to the database.

We need to add this reference to the file

using System.Data.Entity;

11) Now we need to create the connection string.The only place we can do that is by opening the web.config file and adding the following lines of code (inside the   <connectionStrings>   section). I am 

      <add name="FootballerDBContext"
   connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\Footballers.mdf;Integrated Security=True"
   providerName="System.Data.SqlClient"
/>

As you can see from the connection string I am using LocalDB.

Have a look here in order to see what LocalDB is.

12) Now we need to access our model from a controller.This is going to be a simple class that retrieves the footballers data.

In the Controllers folder add the FootballerController.cs file.

Right-click the Controllers folder and create a new FootballerController controller. Have a look at the picture below to set the appropriate settings

Inside the FootballerController controller,  the Index action follows:

 private FootballerDBContext db = new FootballerDBContext();

 public ActionResult Index()
{
ViewBag.Message = DateTime.Now.ToString();
var model = from foot in db.Footballers
select foot;
return View(model.ToList());
}

In the above code, we have created an object of FootballerDBContext and we are querying the Footballers table using a LINQ Query under Index method. We are also passing the result of the Query to our view, so that we can display the data from the Footballers table in our Footballer’s Index view which is generated automatically with the MVC scaffolding system when the controller is created.

The Index.cshtml contents follow:

@model IEnumerable<DemoCachingData.Models.Footballer>

@{
ViewBag.Title = "Index";
}

<h2>Index</h2>

<b>Caching Time:</b>@ViewBag.Message

@*<p>
@Html.ActionLink("Create New", "Create")
</p>*@
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
@Html.DisplayNameFor(model => model.LastName)
</th>
<th>
@Html.DisplayNameFor(model => model.Weight)
</th>
<th>
@Html.DisplayNameFor(model => model.Height)
</th>
<th>
@Html.DisplayNameFor(model => model.JoinedClub)
</th>
<th></th>
</tr>

@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Weight)
</td>
<td>
@Html.DisplayFor(modelItem => item.Height)
</td>
<td>
@Html.DisplayFor(modelItem => item.JoinedClub)
</td>
@*<td>
@Html.ActionLink("Edit", "Edit", new { id=item.FootballerID }) |
@Html.ActionLink("Details", "Details", new { id=item.FootballerID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.FootballerID })
</td>*@
</tr>
}

</table>

When we run for the first time the application the underlying database and table are created.Entity Framework Code First created the database for us. EF detected that the database connection string provided, pointing to a database didn’t exist, so Code First created the database automatically.

From VS you can show all files of the application. Under App_Data special folder you will see the FootBallersDBContext database.

Have a look at the picture below

13) Now let’s add some records in our Footballers table (I added two record straight from the Server Explorer UI) and then run the application. You will see the records displayed on our Index page.

For increasing the performance of our page, we will make use of OutputCache Action filter. As I mentioned earlier, this attribute can be applied either on an individual action method or on the entire controller.

Let’s apply this attribute on our Action Method Index which is available under FootballerController as shown below

[OutputCache(Duration = 60, VaryByParam = "none")]
public ActionResult Index()
{
 ViewBag.Message = DateTime.Now.ToString();
 var model = from foot in db.Footballers
select foot;
 return View(model.ToList());
}

14) Now let’s run the page and check the output by refreshing it multiple times. The page should be cached for 1 minute.

In addition to duration, the output cache attribute supports a number of different settings like VaryByParam.  The default setting for this is star or asterisk which means vary by every parameter possible and that is usually the setting that you want. That's the default because you normally do not want to return the same cached response for different parameters and by parameters think of things like query string parameters. A query string parameter can point to a record you wouldn't want to return the same cached response when someone is looking for record 1 and 2.

15) Now let’s test the VaryByParam attribute. Pass an ID parameter to the Index action methods and modify the code as shown below

[OutputCache(Duration = 60, VaryByParam = "ID")]
public ActionResult Index(int ID)
{
ViewBag.Message = DateTime.Now.ToString();
var model = from foot in db.Footballers
where foot.FootballerID == ID
select foot;
return View(model.ToList());
}

Now run the application again.This time pass an ID as a query string to the URL and see the output of your page. When you pass the different IDs with the URL, the caching will vary based on the IDs. For example –

http://localhost:61850/Footballer/Index/1 will be different than the http://localhost:61850/Footballer/Index/2 

16) You can also set the cache location. The default value here is anywhere meaning it will cache on the server and the client can also cache the result.

You can be very specific and say that the result should only be cached on the server or on the client or on proxy servers in between.

If I want to cache the results on the  server I need to modify the Action Filter of the Index method, like the following:

 [OutputCache(Duration = 60, VaryByParam = "ID", Location=OutputCacheLocation.Server)]

The OutputCache attribute values can be: Any, Client,Downstream, Server, None, or ServerAndClient.

For configuring the cache location, you need to add the System.Web.UI namespace on your controller.

17) There's also a vary by header setting. That allows you to vary the cache based on a specific HTTP header like the language of the browser/client.

18) You can also make use of SqlDependency to invalidate the cache when the cached data gets changed on the Server side.

Finally I want to talk about Cache Profiles.

Cache Profiles are very helpful when it comes to pick the correct cache duration and other cache settings. You can create different cache profiles and use them accordingly to fit your caching strategy. ASP.net allows you to specify a cache profile in the output cache attribute.

The cache profile then is something that's stored in your web.config file and can specify a duration. It's underneath a section named caching and it's inside of here where you can put duration values.

You can have multiple cache profiles and you reference the cache profile by name in the output cache attribute.

It is recommended to use cache profiles instead of hard code the cache settings in the OutPutCache. We avoid repetition in the cache attributes.

If we want to double the duration inside of all of your cache attributes, we don't have to do a search and replace throughout the code. We just go to one location in your web.config file.

It's also easier to change the cache settings once the application is deployed because now all you do is edit the web.config file and then you avoid changing the settings in code, recompiling, and redeploying.

Inside the web.config file in the <system.web> section I can add my cache profile.


<system.web>


<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="CacheMax" duration="3600" varyByParam="none"/>

<add name="CacheMin" duration="20" varyByParam="none"/>
</outputCacheProfiles>
</outputCacheSettings>


</caching>
....
</system.web>

Let’s apply the new CacheProfile (e.g CacheMax) on our Action Method Index which is available under FootballerController as shown below

[OutputCache(CacheProfile = "CacheMax", VaryByParam = "ID", Location = OutputCacheLocation.Server)]

public ActionResult Index()

...

Hope it helps.

1 Comment

  • Nice post. OutputCache however is pretty static. If you want more control over client cache you can have a look at my blog post: https://weblogs.asp.net/wesleybakker/leverage-browser-cache-in-mvc-using-an-action-filter

Comments have been disabled for this content.