In the long list of tips to optimize your website performance are three things: use a CDN to serve content, make as few requests for Javascript and CSS as possible, and minify your Javascript and CSS files by removing whitespace and comments to make them as small as possible. ASP.Net provides a really excellent Bundling and Minification feature to handle the two latter points.This comes at a cost, however. Those static files must live within the project and can no longer be served via a CDN. (Not bundled, anyway.)

For a basic website, ASP.Net Bundling and Minification is fantastic. You set it up in the BundleConfig.cs file as described here, and you get a minified version of your Javascript in as few files as you configure. The Bundling tries to cope with a CDN through the UseCdn property there, but it doesn’t really work, in my opinion. It just spits out the URL on the page to the CDN, so you’re still left with a request for each library if the user doesn’t have the file cached yet. If you’re using something like jQuery UI, for example, it will probably be a request for jQuery, another for the jQuery UI Javascript file, and another for the jQuery UI theme you’ve specified. For these, all you’ve really done is replaced the old ScriptManager with another construct, and the various performance testing tools are still going to shout at you for making too many requests.

One of the guys on my team at work, Allan Bybee, took it upon himself to find a solution to this CDN bundling limitation. All of our Javascript and CSS files were previously being served from a CDN, so all of our static content lived out there rather than in the web project. Moving all of those files back into the project would have been a large task with many compromises to the development experience and web performance at the end. The critical one being that we would gain the bundling at the expense of serving files from the CDN. After a few weeks of digging around in the ASP.Net source code and diligent trial and error, he ended up with a really elegant solution which gives us the best of all worlds - source files that live in the CDN, which bundling watches for changes and then saves the resulting bundle back to the CDN. He did it by extending the built in ASP.Net bundling system, so it’s very similar to that - just improved upon. Here’s the best part: it’s now a Nuget package!

 

Azure Bundling on Nuget

Install from Nuget, and there is just a small amount of configuration in order to get things going. There are six settings that need configuring, and you have a three options for doing this. (Or read the full documentation.)

  1. Web.config - 6 required app settings
  2. Config.json - A file just for the settings
  3. BundleConfig - A different constructor

1. Web.config

<add key=“AzureAccountName” value=“your account name” />
<add key=“AzureAccessKey” value=“your access key” />
<add key=“CdnPath” value=“your cdn or Azure storage url” />
<add key=“SecureCdnPath” value=“your secure cdn or Azure storage url” />
<add key=“CachePolltime” value=“integer value in seconds how often to poll for file changes” />
<add key=“BundleCacheTTL” value=“integer value in seconds for cache time to live” />

2. Config.json

{
“AzureAccountName”: “your account name”,
“AzureAccessKey”: “your access key”,
“CdnPath”: “your cdn or Azure storage url”,
“SecureCdnPath”: “your secure cdn or Azure storage url”,
“CachePollTime”: “integer value in seconds how often to poll for file changes”,
“BundleCacheTTL”: “integer value in seconds for cache time to live”
}

If you use either of those first two configuration options, it will pick them up automatically and your BundleConfig code ends up very, very similar to what it was prior. Set up a variable that defines the Azure blob storage container name where the files live and the bundles will be stored in. (Make sure this container’s access level is not set to private, or none of this is going to work.) That variable is called AzureContainer in the code example below.

bundles.UseCdn = true;
BundleTable.EnableOptimizations = true;
BundleTable.VirtualPathProvider = new StorageVirtualPath(AzureContainer);
bundles.Add(new JSBundle(“~/your virtual path/jqueryVal”, “your folder”).Include(“~/your folder path/jquery.validate.js”, “~/your folder path/jquery.validate.unobtrusive.js”));
bundles.Add(new JSBundle(“~/your virtual path/modernizr”, “your folder”).Include(“~/your folder path/modernizr-2.6.2.js”));
bundles.Add(new CSSBundle(“~/your virtual path/site”, “your folder”).Include( “~/your folder path/bootstrap.css”, “~/your folder path/site.css”));

The key highlights from the code above are setting the VirtualPathProvider property on the BundleTable to an instance of StorageVirtualPath, and changing any ScriptBundle or StyleBundles which you want to be CDN bundles to instead be JSBundle or CSSBundle, respectively.

3. BundleConfig
Set up variables for each of the settings in the BundleConfig, and pass into the constructor for each of the custom bundling classes. (StorageVirtualPath, JSBundle, and CSSBundle.)

BundleTable.VirtualPathProvider = new StorageVirtualPath(AzureContainer, AzureAccountName, AzureAccessKey, CachePollTime);
var jquerycombined = new JSBundle(“~/bundles/jquerycombined”, AzureContainer, AzureAccountName, AzureAccessKey, CdnPath, SecureCdnPath, BundleCacheTTL).Include( “~/your folder path/jquery-2.1.3.js”, “~/your folder path/bootstrap.js”, “~/your folder path/respond.js”));
bundles.Add(jquerycombined);

If you compare that code to the snippet above, you’ll notice the additional arguments being passed in on the StorageVirtualPath, JSBundle, and CSSBundle. These are the variables you’ll want to define with the appropriate values.

One last note on this is the order of precedence on the settings. Assuming you set it up in both the web.config and JSON file, the settings from the web.config will win over the JSON file. If you set it in the BundleConfig file, that will trump having either of the other two set.

This could not have been achieved without Microsoft making the move to open source .Net and ASP.Net. So if there’s something you don’t like about this, or find a bug, or want to contribute additional features to it, here’s the best part - it’s all on GitHub!

 

AzureBundling on GitHub

This project fills a big gap in ASP.Net bundling for those of us that are intensely focused on tuning the performance of websites. Allan has done a tremendous job figuring this out and putting it out there for the world (if you’re reading this Allan, thanks again!) and I hope that other developers find this as useful as I do. If you do, please make sure to report any issues you find, or better still, help contribute to this great project and make it even better!

Comments

Njabulo Mpungose

Good day, thank you for this post it really helped point me in the right direction with an issue I was having, actually this is the solution I needed for my particular requirement.<br /><br />My question is how do you optimize the startup routine as when I have the code setup as in the demo my application is very slow to start up. I have set the code up in debug directives so in debug mode I use my local bundles and in release mode I use the sample code provided. When set to release it&#39;s slow but in debug the application starts up quicker. Any help provided would be greatly appreciated.

Njabulo Mpungose

Comments are closed