Another comment was by Jeff Olson who said that he wanted better integration into an application via an executable which could scan a project and do replacements. He was advocating a similar approach to the one that I had already taken, but instead of specifying files manually, the tool needed to scan a project and compress and combine the needed files. While this is an interesting approach if you wanted a completely platform agnostic solution, but I decided that I would implement it in a bit different manner.
The first requirement that I thought was that it had to work in both ASP.NET and ASP.NET MVC. I also didn’t want to really have any setup or configuration. I also wanted it to output a physical file that I could simply pass a reference to. This way I could avoid having to do any manual caching and such. I just thought it would be easier to deal with. My only concern here revolves around the security of having a file actually written to disk inside of the website process. Some people could have a problem with this, and there could be issues around file locking, but nothing that couldn’t be coded around.
In order to use this, all you really need to do is create folder, throw the Bundler.Framework.dll in it, and then add a reference to the dll:
<%= new Bundle() .AddJs("/Scripts/jquery-1.3.2.js") .AddJs("/Scripts/MicrosoftAjax.debug.js") .RenderJs("/Scripts/Combined.js") %>
Notice that it appends the time that the combined file was generated as a querystring parameter. This is a little trick (which I borrowed from Nate Kohari’s Agile Zen) so that when the file is regenerated, the browser will download this file again instead of using the cached version. (This can be a problem if you have multiple servers all generating this file at potentially different times, and pointing to different files on disk, so I am going to have to rethink how this is implemented)
Another great thing is that if you are in debug mode, then it simply renders out the script tags as you would normally:
Very neat stuff.
So, how does it all work? Well, basically the Bundle class keeps a reference to every file it has output. So when it is called the first time it pulls all of the files and minifies and combines them, writes them out to the disk, and then renders the script tag. When the page is called a second time, the Bundle class simply checks to see if it has already rendered that file, and if it has, then it simply renders the script tag back to the client. If anything changes, just reset the application (this can easily be done by making a change in the web.config to update its date/time) and the Bundle class will lose the cached output for the script tag and then regenerate the combined file.
I’ve simply hacked this up in pretty short order this evening, and so you can go check out the source in GitHub. If you don’t use Git, then just click on the "Download Source" button and they will be happy to zip it up for you! Check it out, let me know if I’ve done anything horribly stupid, and I’d love to hear some feedback!
Loved the article? Hated it? Didn’t even read it?
We’d love to hear from you.
How about adding an extra method which appends a version number rather than the datetime.
Admittedly this would make it a developer task to bump the version but it would mean that in a server cluster you could guarantee the same result across all the servers.
Instead of adding the date when the file was generated you can compute the hash of the generated file contents (and cache it for further requests) and add it as a parameter to the URL.
This fixes the issue that you pointed out for web-farm scenarios.
@Richard Yeah, that was my original thought, but I didn’t really want the developer to have to do the work of incrementing that every time.
@Javi You know, as soon as I saw your response I hung my head in shame because I had originally looking at using hashes to determine if the file needed to be regenerated, but decided that it would be too costly. However, at the time of bundling, generating and storing the hash would be perfect. Excellent idea, thank you.
is it possible to have the concept of
1. resource sets
2. external config
1 is important, since in highly interactive pages i have bunch of includes which handle a particular area of concern. Some of these tend to get shared across views. For example, i have an event site which contains ajax driven google maps for both venues and events. This also contains jquery, my standard util libs etc. I’d prefer to be able to share resources without a 20 line long statement in the view.
2. Mainly for the above (to specify the contents of the resource sets), but also to be able to update resources without a reset.
Leave a comment