ASP.NET Bundling: Run IBundleTransform even when not optimizing - asp.net

We're using System.Web.Optimization bundling to bundle & compress our JS and CSS.
We also use a custom IBundleTransform implementation in addition to to the existing JsMinify and CssMinify to do some fancy stuff to the JS (replacement of certain placeholders) before sending it to the browser.
Everything works fine as long as we're running in Release mode, because then the bundling and optimizing kicks in. But in Debug mode (which is nice for debugging ;) it seems to completely ignore all the specified IBundleTransform (makes sense in most use-cases, I guess).
Is there any way to always run our own IBundleTransform, even in Debug mode, but run the other (default) bundling algorithms (JsMinify, CssMinify) only when I really want to optimize (in Release mode)?

So the debug/release magic is controlled via the Scripts/Styles helpers. The behavior to not apply any transforms is baked into the implementation of these helpers, so if you want to do this, the best workaround might just be to have a debug/release version of each bundle and always enable bundling via BundleTable.EnableOptimizations = true.

if (!HttpContext.Current.IsDebuggingEnabled)
BundleTable.EnableOptimizations = true;

Related

Bundling and minification ASP.NET

I have a huge web application that bundles and minifies a huge amount of javascript and css files.
When we release a new version, we notice that at the first request of each page (controller + view) the software takes a lot of time to respond. So, I started to search a little bit and find out Bundle Caching and it seems when some .js or .css files are changed, the bundle will create a new token. But I have some doubts about it:
When exactly the join and minification of the files is made? It is when it is called at the first time on a view?
There is a possibility that when the software is build, the files will be joined and minified during that process, so when at the first time the virtual path is called, this process already had occurred and cached?
If the slowly problem about my application is not the bundling and minification of the files, what could be?
Thank you.
Note: I'm talking about the process in a production environment. So,
thinks like putting debug=false in the web.config I already have.
1) I would not bet on this one, but I am pretty sure that this needs to be done upfront provided that the version of the bundle is appended to the path as a query string parameter. Since this value is the hash of the bundle result, this needs to be done before any bundle can be downloaded in order for ASP.NET to even be able to add this parameter when you do something like this:
#Url.Content("~/bundles/yourbundle")
Whether it is calculated the first time the bundle url is rendered into the view or at app startup is something I don't know. I still post this answer because I believe I can give you useful information for the 2) and 3) points.
2) It is possible to bundle your files beforehand. You can either use some Gulp or Grunt task, use the Bundler & Minifier extension, or any tool of your preference. In that case, however, you are not required (or even advised1) to use virtual paths as these tools produce physical files. However, you will need to make sure yourself that they are versioned properly so whenever you change some input file, the clients will be forced to download the new one instead of using the one in their cache.
3) Keep in mind that C# is not compiled to machine code. Initial slowness can, and usually is, caused by something called JITting (which is explained in greater detail here), that is, the process of transforming the IL code into machine code. This is a rather lazy process in that it basically happens just before the actual execution of your code. If, for example, you have a method A, it does not get transformed to machine code up until it is actually invoked. Therefore, the first access of every controller/action is slower than subsequent ones (because after the first run, the resulting machine code is kept).
You can also configure your project to compile your views which will cause your app to be slightly faster at the cost of making the build process slower.
1 It is advisable to use physical paths if the files are actually physically present on the disk because like that, you can skip the virtual path resolving process altogether thus making script loading to be a little bit faster.
Bundling and minification is enabled or disabled by setting the value of the debug attribute in the compilation Element in the Web.config file. In the following XML, debug is set to true so bundling and minification is disabled.+
XML
<system.web>
<compilation debug="true" />
<!-- Lines removed for clarity. -->
</system.web>
To enable bundling and minification, set the debug value to "false". You can override the Web.config setting with the EnableOptimizations property on the BundleTable class. The following code enables bundling and minification and overrides any setting in the Web.config file.
C#
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
// Code removed for clarity.
BundleTable.EnableOptimizations = true;
}
Unless EnableOptimizations is true or the debug attribute in the compilation Element in the Web.config file is set to false, files will not be bundled or minified. Additionally, the .min version of files will not be used, the full debug versions will be selected. EnableOptimizations overrides the debug attribute in the compilation Element in the Web.config file

ScriptBundle adding both full and minified file in debug mode

I have the following ScriptBundle defined in BundleConfig.cs-
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/js/yepnope").Include(
"~/Scripts/yepnope.{version}.js"));
}
}
It isn't in fact the only bundle, but that is all in there that is pertinent to the question - the rest is just other bundle definitions.
When in "debug" mode as set in the web.config-
<compilation debug="true" targetFramework="4.5">
Both the full and minified versions of the script are sent to the browser-
<script src="/Scripts/yepnope.1.5.4-min.js"></script>
<script src="/Scripts/yepnope.1.5.4.js"></script>
The script is added using the HTML helper like so-
#section HeadScripts
{
#Scripts.Render("~/js/yepnope")
}
This is an MVC4 project running in Visual Studio 2012 on Windows 7 Professional 64-bit.
Whatever I do I cannot get the {version} wildcard to behave as described in the Microsoft documentation-
Note: Unless EnableOptimizations is true or the debug attribute in the
compilation Element in the Web.config file is set to false, files
will not be bundled or minified. Additionally, the .min version of
files will not be used, the full debug versions will be selected.
For ASP.NET MVC 4, this means with a debug configuration, the file
jquery-1.7.1.js will be added to the bundle. In a release
configuration, jquery-1.7.1.min.js will be added. The bundling
framework follows several common conventions such as:
Selecting “.min” file for release when “FileX.min.js” and “FileX.js”
exist.
Selecting the non “.min” version for debug.
Ignoring “-vsdoc”
files (such as jquery-1.7.1-vsdoc.js), which are used only by
IntelliSense.
The {version} wild card matching shown above is used to
automatically create a jQuery bundle with the appropriate version of
jQuery in your Scripts folder. In this example, using a wild card
provides the following benefits:
Allows you to use NuGet to update to a newer jQuery version without
changing the preceding bundling code or jQuery references in your view
pages.
Automatically selects the full version for debug configurations
and the ".min" version for release builds.
If I add "EnableOptimizations" it seems to behave as expected.
Has anyone else noticed this or found a solution?
MVC4 only knows how to handle a .min.js file. It doesn't recognize -min.js (with a dash).
The way I typically do this, with easy success, is to get rid of any .min.js or -min.js files provided by libraries that provide both a .js and either a .min.js or -min.js. By default, MVC will automatically minify any bundled .js files when you deploy your website, so there's no need to use the provided .min.js or -min.js files.
This isn't necessarily a direct answer to your particular question - it's a way to circumvent the problem entirely.

How to add .min and .debug files in a bundle effectively

I want to take advantage of .min and .debug versions of js and css files.
Consider this bundle for example:
bundles.Add(
new ScriptBundle("~/scripts/vendor")
.Include("~/scripts/jquery-{version}.js")
.Include("~/scripts/knockout-{version}.debug.js")
.Include("~/scripts/knockout-{version}.min.js")
.Include("~/scripts/knockout-{version}.js")
What is the correct mechanism to take advantage of each file. I mean how set some setting to make ASP.NET smart enough to use .min on final releases and .debug while debugging?
Just use .Include("~/scripts/knockout-{version}.js") and ASP.NET will use ~/scripts/knockout-{version}.min.js in production and ~/scripts/knockout-{version}.js when debugging
You can have the bundler switch between debug and non-debug files if you group them with a wildcard. For example:
bundles.Add(
new ScriptBundle("~/scripts/vendor")
.Include("~/scripts/jquery-{version}.js")
.Include("~/scripts/knockout-*")
Then include in knockout-version.debug.js and knockout-version.js in your ~/scripts/ path.
The bundler will use the debug version if you're in debug mode, or minify the non-debug version and use it if optimizations are enabled.

Passing build timestamp to code

I would like to give my CSS and javascript files far-future headers and add a token onto the URLs referrring to them. That way browsers don't have to re-download CSS and javascript unless I've released a new build of the site.
http://example.com/css/styles.css?build=23424
How can I pass a build number or timestamp to my code-behind so it can add the timestamp?
Obviously C# doesn't have macros, which is what I would use in C/C++.
I realise that this will force browsers to download assets whenever I do a new build - regardless of whether or not the build involved changing the assets. However, I would like to put a simple scheme in place before I implement anything more advanced like looking at individual file modification times.
Here's a bit of code that I use to extract the build id from the current assembly at application start. It reads the version number from the assembly, a version designator (dev/qa/blank) from the web config, then constructs a version number string to stuff into the application. This code goes in Global.asax.cs. You can then refer to it in your markup from the Application instance.
var webAssembly = Assembly.GetAssembly( typeof(...known class...) );
var designator = WebConfigurationManager.AppSettings["VersionDesignator"];
string version = webAssembly.GetName().Version + designator;
this.Application.Add( "Version", version );
Here's an example of how you could use it in an MVC context (sorry I don't have any WebForms examples).
<link type="text/css" rel="stylesheet"
href="/Content/styles/screen.css?build=<%= this.Application["Version"] %>" />
Interesting idea.
Here's one way to do it:
Go into your AssemblyInfo.cs class under the Properties folder in your project.
Change your assembly version to include the star wildcard: [assembly: AssemblyVersion("1.0.*")].
In your code, you can retrieve the current build version like this:
_
var buildNumber = Assembly.GetExecutingAssembly().GetName().Version.Build;
_
That's it, you're done.
Changing the AssemblyVersion to 1.0.* will cause Visual Studio/MSBuild to increment the build number automatically for you each build. Then, you can access this build number at runtime using the above code snippet. Easy as cheese.
Browsers and servers know how to handle caching via HTTP. So, I misunderstand, what are you trying to accomplish with providing this kind of caching. A browser will not re-download your CSS and Javascript if it has seen it recently. And it will re-download it if you do a push of your new build on the server. See cache headers like Cache-control and Expires etc. Here's a tutorial http://www.mnot.net/cache_docs/

ASP.NET - script and css compression

Is there any native compression (for javascript/css files) available in ASP.NET?
Try Chirpy. It mashes, minifies, and validates your javascript, stylesheet, and dotless files.
You can use YUI Compressor or Google Closure Compiler.
http://chirpy.codeplex.com/
Or, for more info, check out:
http://www.weirdlover.com/2010/05/22/visual-studio-add-in-for-dotless-js-and-css-files/
In the appendix of Professional ASP.NET 3.5 Scott Hanselman talks about Packer for .NET. This will integrate with MSBuild and pack javascript files for production deployments etc.
You could use Packer.
This utlity supports JavaScript compression and /or "minifying", and CSS "minifying".
It's available as a command line utility or also as an MSBuild task.
This way you can integrate it into your build process / Visual Studio project.
There is Gzip/Deflate compression support in IIS compatible with all modern browsers except IE6. For IIS 7 check this page: http://www.iis.net/ConfigReference/system.webServer/httpCompression
Further to other answers and comments, you can use Yahoo!'s YUI Compressor and make it an MSBuild Task to integration it into your build and deployment process.
Try StyleManager for CSS combination and minification. It uses YUI Compressor under-the-hood.
Its usage is a lot like asp.net's ScriptManager, so it's quick to get used to. It's easy to add to your project too, only takes a minute.
Most importantly - it combines your CSS files too. So instead of having like 10 CSS files to download it'll just be 1, which will also be compressed etc.
I have written something to do this for me, you can download it here:
http://www.picnet.com.au/blogs/Guido/post/2009/12/10/Javascript-runtime-compilation-using-AspNet-and-Googles-Closure-Compiler.aspx
It uses Google's closure compiler which is pretty awesome.
Thanks
Guido
Here is my way:
Use MVC.
Process js|css content via MVC controller's actions.
Combine multiple files into one.
Minify and obfuscate script|css on fly before it stored in cache.
Cache results.
Use CacheDependency.
Enable gzip for dynamic content.
Enable gzip before cache feature.
Everything can be done just by adding custom attributes on action methods, using ASP.NET MVC Js/Css Composer/Compressor.
Sample:
public class JsController : Controller
{
[Utility.Processors.JsCompress]
[OutputCache(Duration = 3600)]
public ActionResult Jquery()
{
return View();
}
}
You can derive from CustomTextPostProcessingAttribute and make your own postprocessing for any type of text content, you need.
I just learned something today: you can run JavaScript via windows console. I'm a fan of cssmin.js; so, this plus windows console = win! All you have to do is download cssmin.js, put it in a folder on your web project and add the following post-build event in Visual Studio:
type "$(ProjectDir)css\*.css" | cscript //NoLogo "$(SolutionDir)tools\cssmin.js" > "$(ProjectDir)css\core.min.css"
Doing this keeps you from having to edit your project as ajaxmin would have you to do.

Resources