Edit: There is now a screencast version of this post available.
Since ASP.NET 4.5 in 2012, developers have had access to a pair of tools in their ASP.NET toolbox called “bundling and minification”. This feature would direct the webserver to combine together CSS or JavaScript files into one extra-large file and then apply a minification algorithm to shrink the size of the file for delivery. In ASP.NET 5 however, this feature is no longer available. In this article, I’ll review where bundles were configured and how you can migrate them to the recommended deployment model for ASP.NET 5
In practice, ASP.NET bundles are an easy tool to configure with an entry in the App_Start folder typically called BundleConfig.cs that contained directions for which files to operate on:
We could then reference these bundles in our code with syntax like the following:
That’s actually really easy to use, and ASP.NET will replace those calls to Styles.Render and Scripts.Render with appropriate references to the components of the bundle when running in Debug mode. When my project is running in Release mode, the output is converted to a reference to the concatenated and minified files:
Very cool… the contents of those files are all mashed up and made machine-readable. There is however one drawback: those files are created on the fly by the web server and cached in memory. This could be a processor and memory consuming task that we don’t want to perform that frequently.
What are the new tools available in ASP.NET 5?
Starting with ASP.NET 5, Microsoft is encouraging developers to start integrating some more popular web development tools that other web developers have been using: Gulp, npm, and bower. Each of these tools has a specific purpose:
- Gulp is a task-runner written in JavaScript that runs on top of the NodeJS framework and automates repetitive tasks
- npm is the Node Package Manager, and it can be used to deliver plugins and utilities that run in the NodeJS framework.
- Bower is a package manager for delivering static resources from Git repositories.
In a brand new ASP.NET 5 web application that’s built with the standard web site template, these three tools are pre-configured to deliver some content for you:
By default, npm is configured to provide the tools necessary to run gulp and bower. Bower is configured to deliver the following standard JavaScript and CSS frameworks:
Gulp is configured to fetch these libraries with bower and deliver them into the wwwroot/lib folder, with folders for each library. Nice… but that doesn’t do anything with MY files that I’ve written.
Lets Start Bundling My Files
In this sample scenario, I have two files in the wwwroot/app folder called app.js and menu.js that I want to bundle and deliver to my customers. To accomplish this, I need to give gulp the extensions it needs to concatenate, rename, and minify my files. I’ll do this by asking npm to deliver three gulp extensions. This is accomplished by updating the package.json file that npm uses:
Once I save this file after I add the gulp-concat, gulp-uglify, and gulp-rename line items, Visual Studio will automatically call npm and add these packages to my project. I can then start writing a task for gulp to operate on my two JavaScript files. This task is added to the end of my gulpfile.js:
That was easy… and when it runs it will deliver a pair of files for me: dist/all.js and dist/all.min.js This task can be wired up to run before or after build processes, when the project is cleaned or whenever the project is opened using the bindings in the Task Runner Explorer:
These bindings are assigned by right-clicking on the task name on the left side and selecting which event I want the task to run on. From that same right-click gesture there is an button to force the task to run now.
How do I tell ASP.NET to use the bundled script in Production, but not Development?
This is the question we solved previously by deriving the state of the project with the Debug or Release mode flags. That’s not going to work here, because these are static files that are residing on disk, and the ASP.NET pipeline isn’t involved in processing them… gulp already did that heavy lifting for us. We can task care of this with the new environment TagHelper.
I’ve done a number of posts recently about the introduction of TagHelpers in ASP.NET 5 and MVC 6:
- Get Started with ASP.NET MVC TagHelpers
- ASP.NET MVC Tag Helpers – Web Controls 2.0?
- ASP.NET Tag Helpers – The Basics
- ASP.NET Tag Helpers – Making HTML More Cool!
In this case, the Environment Tag Helper comes from the standard ASP.NET TagHelpers collection. To use this tag, add the following @addTagHelper directive at the top of your razor view:
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
You can also place this directive in a special view called “_GlobalImport.cshtml” in your project.
Next, use the environment tag with its names attribute to wrap the various script or link tags that you need for each environment name that your web application will run in:
Where does the name for the environment come from? How can I change that name when I deploy my application to another machine? The answer is in the environment variables set on each machine when the application runs. Specifically, the environment name is loaded from the ASPNET_ENV variable. Visual Studio 2015 does a great job in the project configuration screens to configure this value at debug time:
We set that value here, or with standard set commands on the operating system that the application runs on.
You can now clearly connect the dots and see that the environment tag helper will only include its contained elements if the ASPNET_ENV value matches one of the comma-delimited values in its names attribute.
Summary
The bundling capabilities of ASP.NET 4.5 have been dropped, and the replacement is very easy to use. Support for gulp, bower, and npm is available from all of the normal sources on the web like Stack Overflowand GitHub issues. With these recommended tools, you can be more productive and your web server won’t have to do the repetitive bundling and compression tasks for your static resources.