I was working an an internal project that started on MVC 2, and has recently been upgraded to MVC 4. There's a lot of house keeping to do to keep the codebase maintainable.
One custom piece of code that was implemented to make sure assets like javascript and css were not cached when a new version was released was implemented as follows:
<link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/themes/base/jquery-ui.css" + Versioning.Latest)" /> <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/kendo/2012.2.913/kendo.common.min.css" + Versioning.Latest)" /> <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/kendo/2012.2.913/kendo.metro.min.css" + Versioning.Latest)" /> <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/Site.css" + Versioning.Latest)" /> <script type="text/javascript" src="@Url.Content("~/Scripts/kendo/2012.2.913/jquery.min.js" + Versioning.Latest)"></script> <script type="text/javascript" src="@Url.Content("~/Scripts/kendo/2012.2.913/kendo.all.min.js" + Versioning.Latest)"></script> <script type="text/javascript" src="@Url.Content("~/Scripts/kendo/2012.2.913/kendo.aspnetmvc.min.js" + Versioning.Latest)"></script> <script type="text/javascript" src="@Url.Content("~/Scripts/jquery-ui-1.9.2.min.js" + Versioning.Latest)"></script> <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.formatCurrency-1.4.0.min.js" + Versioning.Latest)"></script> <script type="text/javascript" src="@Url.Content("~/Scripts/date.js" + Versioning.Latest)"></script> <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.watermark.min.js" + Versioning.Latest)"></script> <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.cross-slide.min.js" + Versioning.Latest)"></script> <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.cookie.js" + Versioning.Latest)"></script> <script type="text/javascript" src="@Url.Content("~/Scripts/browser.support.js" + Versioning.Latest)"></script> <script type="text/javascript" src="@Url.Content("~/Scripts/handlebars.min.js" + Versioning.Latest)"></script>
Where Versioning.Latest is defined as follows:
public class Versioning { public static string Latest { get { return "?v=" + typeof(Versioning).Assembly.GetName().Version; } } }
This works, however, it will cause a redownload of all javascript/css files when a new version is deployed, even if the file hasn't changed. Third party javascript libraries such as KendoUI, jQuery and date.js are unlikely to change (particularly if you're referencing a particular version of the library).
I updated this code to use the new content bundling features that were introduced in MVC 4. Here's the steps:
1. In the NuGet Pageage Manager Console, grab Microsoft.AspNet.Web.Optimization
Install-Package Microsoft.AspNet.Web.Optimization
2. Create a BundleConfig.cs and define your bundles
public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { // Scripts bundles.Add(new ScriptBundle("~/bundles/kendoui").Include( "~/Scripts/kendo/2012.2.913/jquery.min.js", "~/Scripts/kendo/2012.2.913/kendo.all.min.js", "~/Scripts/kendo/2012.2.913/kendo.aspnetmvc.min.js")); bundles.Add(new ScriptBundle("~/bundles/thirdparty").Include( "~/Scripts/jquery-ui-*", "~/Scripts/jquery.formatCurrency-*", "~/Scripts/jquery.watermark.min.js", "~/Scripts/jquery.cross-slide.min.js", "~/Scripts/jquery.cookie.js", "~/Scripts/date.js", "~/Scripts/browser.support.js", "~/Scripts/handlebars.min.js")); // Stylesheets bundles.Add(new StyleBundle("~/Content/css").Include( "~/Content/themes/base/jquery-ui.css", "~/Content/Site.css")); bundles.Add(new StyleBundle("~/Content/kendoui-css").Include( "~/Content/kendo/2012.2.913/kendo.common.min.css", "~/Content/kendo/2012.2.913/kendo.metro.min.css")); } }
3. Call the bundle config from Global.asax Application_Start to register the bundles
protected void Application_Start() { : BundleConfig.RegisterBundles(BundleTable.Bundles); : }
4. Update your _layout.cshtml to reference the bundles
<link href="@Scripts.Url("~/Content/css")" rel="stylesheet" type="text/css" /> <link href="@Scripts.Url("~/Content/kendoui-css")" rel="stylesheet" type="text/css" /> <script src="@Scripts.Url("~/bundles/kendoui")" type="text/javascript"></script> <script src="@Scripts.Url("~/bundles/thirdparty")" type="text/javascript"></script>
5. Update your (root)\web.config and Views\web.config to register the System.Web.Optimization namespace
<pages pageBaseType="System.Web.Mvc.WebViewPage"> <namespaces> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Routing" /> <add namespace="System.Web.Optimization" /> </namespaces> </pages>
6. And you're done!
Run your application and look at the generated URLs for your CSS and JS. They contain a hash in the URL that updates when the bundle changes:
<link href="/Content/css?v=DW4VA8QHWz0lpg-1xgEkBu9a2jl0u21oskVI8gjXdO81" rel="stylesheet" type="text/css" /> <link href="/Content/kendoui-css?v=NOcY8mlFVB5YWf-VVYSPB17Ov0AXjKcYHOg76QV3vqQ1" rel="stylesheet" type="text/css" /> <script src="/bundles/kendoui?v=YbNJGKtU_KjafyxKAw3U4QVNX021Qk1BCIWYBvui2n41" type="text/javascript"></script> <script src="/bundles/thirdparty?v=kArVyDzIkXBQ9DtFhv4FWSKAx1YfHKB4UlIG_6cJmVY1" type="text/javascript"></script>
If we update the Site.css and rebuild, only the /Content/css has changes, all the other hashes remain the same.
<link href="/Content/css?v=tynpQ3xqzP2PGlfCEDcWZa9rnxNaQPSPSBl2yxmF_gI1" rel="stylesheet" type="text/css" /> <link href="/Content/kendoui-css?v=NOcY8mlFVB5YWf-VVYSPB17Ov0AXjKcYHOg76QV3vqQ1" rel="stylesheet" type="text/css" /> <script src="/bundles/kendoui?v=YbNJGKtU_KjafyxKAw3U4QVNX021Qk1BCIWYBvui2n41" type="text/javascript"></script> <script src="/bundles/thirdparty?v=kArVyDzIkXBQ9DtFhv4FWSKAx1YfHKB4UlIG_6cJmVY1" type="text/javascript"></script>