Monday, July 16, 2012

How to force to update JavaScript and CSS files after deploying new version of ASP.NET MVC application.

Usually after each deployment my ASP.NET MVC (Razor view) site I have a little mess with cached JavaScript and CSS files. Sometimes user's browsers show unappropriated view or some client functionality doesn't work.
Yeah, it is very easy to fix - just clear browser cache or reload JavaScript and CSS files (press CTRL+F5). But I want user to avoid this annoying action moreover not all users are so familiar with PC to know this trick.
My friend Mykhailo found nice solution for this. If I add parameter to JavaScript's url and will change parameter's value after each deployment. Browser will recognize it as other script and will update file.
So now I should add something like "?v=xxxx" to all links of CSS and JavaScript files in my project and keep in mind it for future links. Looks not really smart solution doesn't it?
First of all I want to say that I set JavaScript and CSS files in "Razor" style. For instance:


<link href="@Url.Content("~/Content/Site.css")" rel="...

In this case I just need to find way override "Content" method. Ok I've done it:

public class UrlHelperEx : UrlHelper
{
	#region Constants
	private const string c_VERSION_FORMAT = "{0}?v={1}";
	#endregion
 
	#region Initialization
	public UrlHelperEx(RequestContext requestContext)
		: base(requestContext)
	{
	}
	#endregion
 
	#region Public methods
	public new string Content(string contentPath)
	{
		var content = base.Content(contentPath);
		Version version = WebHelper.GetVersion();
		return string.Format(c_VERSION_FORMAT, content
				, version.Build.ToString(
				CultureInfo.InvariantCulture));
	}
	#endregion
}

Please, pay attention - I've used "new" key world for "Content" method.
Razor's view bases on "WebViewPage" and "WebViewPage<T>" classes. So I extended to use new "UrlHelper" class them.
public abstract class WebViewPageEx : WebViewPage
{
	#region Properties
	public new UrlHelperEx Url
	{
		get;
		set;
	}
	#endregion

	#region Public methods
	public override void InitHelpers()
	{
		base.InitHelpers();
		Url = new UrlHelperEx(ViewContext.RequestContext);
	}
	#endregion
}
public abstract class WebViewPageEx<T> : WebViewPage
{
	#region Properties
	public new UrlHelperEx Url
	{
		get;
		set;
	}
	#endregion

	#region Public methods
	public override void InitHelpers()
	{
		base.InitHelpers();
		Url = new UrlHelperEx(ViewContext.RequestContext);
	}
	#endregion
}
Pay attention - I've used "new" key  again. So I've replaced method and properties from base classes and I don't need to do any changes in my views.
Now I need to replace base classes for views in my project. How to do it I've found in the this great article - "Changing Base Type Of A Razor View". So I replaced next line in Web.config that is located in Views folder.

 <pages pageBaseType="System.Web.Mvc.WebViewPage">
 
with 
 <pages pageBaseType="MyNamespace.WebViewPageEx">
 
And version of current build is added to each CSS and JavaScript's url in my project now.
It was "pros" and now a few words about "cons". I have a lot of "default" CSS and JavaScript files. For instance of jQuery, third part components and so on. I don't change this files from build to build but they are updated after build. It is possible to implement some extra logic in "Content" method for skipping such files or use old school way adding url. for instance:


<link href="../../css/custom-theme/jquery-ui-1.8.5.custom.css" rel="...

But in my opinion it is whims and current implementation is enough.

PS: I'm pretty sure that it is possible to do something like for any view engines in ASP.NET MVC.

4 comments:

Unknown said...

Some time ago I faced with the same problem. There is another solution: more quick, but not so 'clear', smth like that:

string.Format("~/Scripts/myscript.js?date = {0}", DateTime.Now))" type="text/javascript">

RredCat said...

Yeah, it easy solve issue. But you don't use caching benefits of browser in this case. Using version of Build looks better for this way.

Anonymous said...

thank you. nice coded

Unknown said...

This is a great post.I think it will be very usefull to us. I read it but I need some thing more to know about this.

Mobile Application Development Company