How to disable caching on the server and client side in ASP.NET MVC

On of the problems that i encountered in the deployment of my sites to IIS is that sometimes my changes regarding the html and javascript files just did not change even hours after the deployment.

After trying several things i realized that only when i disabled the caching on my Chrome development tools the problem was fix. This solution to disable the cache is nice when you have only one user to your web site but to ask hundred of users to turn off their caching is some how problematic.

My solution consists of two parts: first is to cancel the over all caching with an attribute that can be placed on an action or a controller. This solution might do the work but for someone who uses Angular.js another solution is needed to completely disable the caching

Disable the cache with an attribute

I create an attribute called No cache in a separate file:

using System;
using System.Web;
using System.Web.Http.Filters;
using System.Web.Mvc;
 
namespace MyTest.Attribute
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public sealed class NoCacheAttribute : System.Web.Mvc.ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
            filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
            filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
            filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            filterContext.HttpContext.Response.Cache.SetNoStore();
 
            base.OnResultExecuting(filterContext);
        }
    }
}

Then, i use this attribute on a wanted controller or action (in my case controller) like this:

[NoCache]
public class HomeController : Controller
{
}

This will tell the server to send the response to the client with the caching header set to no caching. The reason for so many settings in this attribute is so that this attribute can be cross browser affective and work on as many browsers possible

For the ones that use Angular.js and use templates to build directives this might be not enough because i found out that Angular has an internal mechanism that caches these templates. The only thing that worked for me is to add each template a date and time stamp as a query parameter, this wont change the functionality because query parameters have no effect on html files.

You need to cascade this code to you angular app and it will do the trick

.config(['$provide'function configureTemplateFactory($provide) {
    // Set a suffix outside the decorator function 
    var cacheBust = Date.now().toString();
 
    function templateFactoryDecorator($delegate) {
        var fromUrl = angular.bind($delegate, $delegate.fromUrl);
        $delegate.fromUrl = function (url, params) {
            if (url !== null && angular.isDefined(url) && angular.isString(url)) {
                url += (url.indexOf("?") === -1 ? "?" : "&");
                url += "v=" + cacheBust;
            }
 
            return fromUrl(url, params);
        };
 
        return $delegate;
    }
 
    $provide.decorator('$templateFactory', ['$delegate', templateFactoryDecorator]);
 
}])