A couple of months ago, I blogged about how to improve your YSlow score under IIS7. One of the recommendations from the Yahoo! best practices guide against which YSlow rates your site is that you should set far-future expiry dates for resources such as scripts, images and stylesheets.
This is good advice, and straightforward to implement under IIS7, but can leave you with a bit of a problem – once you’ve released your site with all of your scripts and stylesheets set to expire next millenium, you can no longer edit them as you please. If you change them and release them with the same name, returning visitors to your site will not see the changes – they will use the cached version that you told them was good for another 1000 years.
Instead, if you need to change one of your files, you need to give it a new name so that clients who downloaded and cached the original version are sure to get the update when it’s deployed. This means you need to update any references to point to the new file, which has the potential to be a bit of a management nightmare.
Obviously, if you’ve only got one or two of each type of file and you’ve referenced them from a master page or similar shared component then you can handle this manually. However, if you’ve got more than that, or if you’re following the recommendation to combine and pack files, then life can get more fiddly.
On a previous project, I’ve used the Aptimize Website Accelerator, which handles all this stuff for you in an extremely slick way. However, for projects that don’t use this, I wanted to come up with a way of making the process easier.
In order to do this I decided to take advantage of ASP.NET’s internal URL rewriting feature, exposed via the HttpContext.RewritePath method. What I ended up with – which you can download from Codeplex if you’d like to give it a try – has two parts, the logic to add and remove version numbers from file names and an HttpModule to apply this logic to incoming Urls. The idea is that the code writing out links to the JS/CSS files can pass those filenames through the provider to add a version number, and the HttpModule will intercept incoming requests for those files and rewrite them to point to the correct filename.
The resourcing versioning provider
Whilst I’m not a massive fan of the ASP.NET provider model, I’ve recently been feeling the pain of using a variety of different open source projects with overlapping dependencies on different versions of the same tools. I therefore decided to use the provider model to allow me to swap out different ways of generating the version number.
The first important class here is ResourceVersioningProvider. This defines the two methods that providers need to implement.
It should be pretty obvious what they do from their names.
I’ve then got a base class, ResourceVersioningProviderBase which contains the code that’s independent of how the version number is generated (basically the filename parsing/modifying logic as well as some caching.) This class is abstract and defines a single abstract method, GetVersionNumber(). This should return the version number that’s going to be used to make the filename unique.
The final relevent class is AssemblyBasedResourceVersioningProvider, which inherits ResourceVersioningProviderBase and implements GetVersionNumber by returning the full version number of an assembly specified in the configuration. The configuration itself looks like this:
As you can see, there’s also an attribute that controls which file extensions the provider will add version numbers to. This means that if you call “AddVersionToFileName” and pass in an a filename with a different extension, no version number will be added.
The resource versioning HttpModule
The HttpModule that forms the other half of the solution is pretty straightforward. Every incoming Url is cracked open using the Uri.Segments property, and the final segment (assumed to be the requested file name) is passed into the provider to remove the version number, if present. If the provider returns a filename that differs from the one in the request url, it is used to build a new Url that can be passed to HttpContext.RewritePath. Processed Urls are cached (regardless of whether or not they require a rewrite) to improve performance.
An Alternative Approach
Despite having written this post a month or so ago, I was only spurred into posting it by reading about this alternative approach from Karl Seguin. He’s got an interesting series going on at the moment covering ASP.NET performance – it’s well worth a read. I can see that it would be fairly straightforward to replace my AssemblyBasedResourceVersioningProvider with a FileHashBasedResourceVersioningProvider, although given that no. 1 on my to do list at the moment is learning NHibernate, it might be a while before I get round to it!
A couple of months ago, myself and some colleagues had the pleasure of pushing the metaphorical big red button and seeing www.fancydressoutfitters.co.uk go live. We all agreed it was one of the best projects we’d worked on in a long time, for a number of reasons. Those reasons can be split into two categories: the team and the technology.
The core dev team was mostly made up of people who’d worked with one another before. For example I first worked with James back in 2002, well before either of us joined Conchango (as it then was.) James had spent a lot of time working with Howard on Tesco Digital, and although I’d never worked with Howard directly I’d had a lot of contact with him on my previous project. We’d all previously worked with Ling (our data architect), Justin (developer, yet to create his own online presence for reasons unknown) and Naz (tester, ditto) on other projects over the previous 3 years.
On most projects, the team spends a fair amount of time up front becoming effective, and a big part of that is getting to know one another – strengths, weaknesses, the way we all communicate and so on. For us, there were only a couple of people that none of us had worked with on previous projects, and that made it simple for the team as a whole to gel really quickly. The process was simplified even further by a great project manager who knew how to make things happen without needing to control the dev team, and a business analyst who managed to take a huge amount of grief from us all about his gratuitous use of clip art in his Powerpoint presentations whilst at the same time winning the respect of the client for the speed with which he understood how they worked internally.
Also, because the team was well resourced up front, Howard and James were able to spend the time laying the appropriate foundations. James has talked about this previously, so I won’t cover it again, but suffice it to say that the time invested up front paid massive dividends later in the project.
The foundations they laid included the core software stack we would use and the tools and techniques we would adopt. They decided that the core of the site would be based on ASP.NET MVC and S#arp Architecture, which is a best practice framwork joining MVC with NHibernate. They also picked some other bits and pieces to reduce friction – things like Spark, AutoMapper and xVal – and James decided that the project would use Behaviour Driven Development as a testing approach.
When all of these things came together, one thing that stood out was how clean the resulting solution became. Core design concepts such as separation of concerns, encapsulation and dependency injection were baked in, making the code clean and easy to test. The layers of the solution were well defined and understood, meaning it was always obvious where a particular piece of code should live and it was easy to discuss the codebase between us because we were all talking the same language. The adoption of BDD finally made using a test-driven approach make sense for those like me who always found it a challenge.
As the project neared it’s end, myself, James and Howard wrote some blog posts talking about various aspects of the solution, but it became hard for all of us to pull out specific bits of code to talk about because removing them from the wider context of the solution caused them to lose their meaning. Howard suggested that we could address this – as well as giving something back to the community that gave us such great software – by putting together an app based on the same architecture and publishing the source code. We could then use it to talk around specific features or areas of the architecture, and at the same time demonstrate to the community the approach we took for the FDO site.
A couple of years back, Howard spent a few hours one evening creating an app called “Who can help me?” Originally intended to capture information about the skills and experience available within Conchango, it took him about 3 hours to put together using ASP.NET WebForms and Linq to SQL. It was the ideal application for our purpose; it has a practical application (we still use it inside EMCC) and it’s simple so the focus can be on the solution design rather than the business logic.
So, here it is: Who Can Help Me, built using S#arp Architecture, NHibernate, Spark View Engine, xVal, AutoMapper, Castle Windsor, MSpec, RhinoMocks and PostSharp. We’ve pulled in some new bits, such as MEF and DotNetOpenAuth, hooked it up to Twitter using TweetSharp to demonstrate the pattern for external service integration, and published the whole lot on Codeplex.
The result is definitely more complex than the problem requires, but that’s ok – as I mentioned above, we specifically chose a simple application to demonstrate an enterprise level architectural approach with the hope that the focus could be on the latter. So please have a play with the live site, download the code, give it the once over and let us know what you think. We’ll be blogging about the bits we find interesting (links below), as well as tidying up the bits we’re not so keen on and we’d love to hear any feedback you have.