ASP.NET Performance Framework

At the start of the year, I finished a 5 part series on ASP.NET performance – focusing on largely generic ways to improve website performance rather than specific ASP.NET performance tricks. The series focused on a number of topics, including merging and shrinking files, using modules to remove unecessary headers and setting caching headers, enabling cache busting and automatically generating cache busted referneces in css, as well as an introduction to nginx.

Yesterday I managed to put a number of those things together into a framework which I hope will make it easier for developer to leverage these best practices. The project is called Metsys.WebOp, and you can download it from github. It comes with a sample application, which not only shows how to use the framework, but also has all the documentation you’ll hopefully need.

The first part of the framework is a console application which is meant to be run as part of your build process. This is driven by a text file which supports 4 commands – merging files together, shrinking them, generating cache busting hashes and pre-zipping files. Here’s what the sample app’s command file looks like:

#combine JS files
combine: js\all.js: js\jquery.js, js\jquery.rollover.js, js\web.js

#combine css files
combine: css\merged.css: css\reset.css, css\main.css

#shrink the generated files
shrink: js\all.js, css\merged.css

#generate cache-busting resource hashes
busting: hashes.dat

#pre-zip our files
zip: js\all.js, css\merged.css

The next part is meant to be used from within a MVC application (it wouldn’t take too much effort for someone to make it work with WebForms) – first by allowing you to configure the runtime component, and then by providing extension methods to HtmlHelper. Essentially this gives you 4 methods, Html.IncludeJs, Html.IncludeCss, Html.Image and Html.ImageOver. You can also toggle debug mode, which’ll make all of this transparent during development (nothing worse than dealing with merged and shrank files in development).

The last part are a couple HttpModule which make everthing possible. The Zip module will returned the pre-zipped files (generated by the build process) should the browser accept zip files. The WebOp module will remove unecessary headers and add caching headers to js, css and images – only really practical if you are also using the cache busting featuers.

You can download the project from http://github.com/karlseguin/Metsys.WebOp.

You might also be interested in checking out the mscd project, which does a lot of the same stuff, but is probably more mature.

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

9 Responses to ASP.NET Performance Framework

  1. dario-g says:

    Thanks for the mention MSCD.

    PS
    It’s easy to use aka Plug&Play 😉

  2. TadeuszWojcik says:

    Thank’s for good advices :)

  3. karl says:

    My experience with deployment is that it varies from project to project. I think, as a general rule, a single project with your controller, binders, view and assets is the right “default” choice. Splitting things up should only be done because there’s an actual need.

    For example, at my work, we’ve split up our views/css/js from our controllers/binders because of our QA and verification process (medical equipment).

    I’ve also done weird stuff to improve reuse of assets across different MVC sites (its a shame better support for this doesn’t exist in VS.NET and in MVC itself).

    You also bring up a valid case – when deploying to multiple servers.

    Again, these more complicated setups should be reserved for situations that actually require them.

    A lot o this stuff can also be scripted out..either through a build script or a custom console app.

  4. TadeuszWojcik says:

    @Karl, Thanks :) you’re the man ! Please tell me one more think, how would you organize content in your mvc project? would you add another with only static files? Doing that way you could deploy that project other server . Any other ideas? Thank again :)

  5. karl says:

    @Tadeusz
    I just checked in a change that I hope will help you do what you want (which is a good idea). The configuration’s RootAssetPath now expects 2 parameters..the relative and absolute path. Initially they were assumed to be the same, but now you can do something like:

    c.RootAssetPathIs(“http://cdn.mysite.com/assets/”, Server.MapPath(“/assets/”)

  6. TadeuszWojcik says:

    Hi,
    Karl how would you approach keeping static content in different domain? what would need to be changed in your framework to make this possible?

  7. Tomas says:

    I also did something similar in WebForms for a year or so. One improvement I had was that I combined all the files on the fly, and generated a new virtual file that I cached. The new filename consisted of a guid calculated from the filenames and the extension. One of the main advantages with this improvement is that you can have an unlimited cache on the files since the guid will change as soon as one of the files changes content.

    The original files where grouped in a controller that handled almost everything. You could specify in config if you wanted to combine files or/and zip them, you could also on file level in the controller override the setting in the config for debugging purpose to exclude one file from the combined file which turned out to be useful when debuggin production environment.

  8. Dan Martin says:

    Thanks!

  9. Rob says:

    Done something similar, but one step further using Ruby. I’ve written build conventions for our js/css.

    Ruby checks the project type (by its Guid) and if it’s a web type one, looks for a “res” folder.

    It then finds all *.merge.css and *.merge.js files. And passes them to a Ruby app called Juicer. With Juicer you can use @imports in css, and it’s clever enough to actually get that file and spit it into the calling file. It then runs YuiCompressor and can do soft or hard cache-busting.

    I’ve even started writing a Ruby script that calls SVN, and get the revision number of each image (stores the result once found for speed), and puts the number into the url (hard cache-busting).

    Just means we don’t have to remember to add anything to a config, we just need to make sure we follow convention and it’ll get included.

    I keep meaning to blog on it, but want to get 100% coverage on my rake/ruby stuff before I do. Some of the earlier stuff wasn’t covered too well.