Attempt to do View Compilation for Azure Web Role

We are hitting the deck with our site performance and optimisation. It is fast, but we want it uber-fast! So next stage is to have IIS up and active all the time with all the views being compiled and ready before any user comes to them.

By default, IIS compiles views only when a request for that view comes in. So first time a user visits some rare page in your application, user is waiting a bit longer while IIS does Just-In-Time compilation. And actually if you look under the hood IIS does stacks of things before it shows you a web-site.

Despite of common believe, IIS does not run your web-application from /bin folder, it copies all required files to a temp folder. To be more specific, it copies files to c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\. Reason for that – file locking. For just-in-time compilation, it needs to update binaries, but in /bin folder binaries can be locked.

So if you see very strange behaviours on your web-app, and clean-rebuild in VS does not help anymore, you go to that folder and clean everything up. This will force IIS to restart your web-sites and re-copy all required files.

I digress, back to View Compilation. When you do Web-Publishing, you can tick a checkbox to force views to be pre-compiled:

When you publish to Azure Web Roles you don’t have this option. And this makes me sad. So we need to do a work-around with some sort of hack.

One of the options (very popular on StackOverflow) is to use RazorGenerator package. I don’t like that approach for following reasons:

  1. Razor Gen requires a Visual Studio Extension. This means getting all developers to install this. We already have performance issues with VS. Yet another extension is not going to improve it.
  2. Razor Gen adds a generated .cs file for every view. On 600 views, have another .cs file? VS is slow enough with 180K lines of code. Add another 600 generated classes there? No thanks.
  3. We do some crazy things with view engines on per-tenant basis. Will that work with RazorGen? No idea, but certainly not really looking forward debugging this.

On stack I found a few answers that talk about ClientBuildManager.PrecompileApplication() (MSDN). This looked promising.

This method basically does JIT compilation for your entire web-app and copies outputs to \Temporary ASP.NET Files. Here is what I did to get it to work:

public class WebRole : RoleEntryPoint
{
    public override bool OnStart()
    {
        // this creates IIS Server Manager that works with IIS configuration
        // We need this to extract Site ID from IIS
        using (var serverManager = new ServerManager())
        {
            // this only works inside of Azure Role
            var siteName = RoleEnvironment.CurrentRoleInstance.Id + "_Web";

            // gets object corresponding to our IIS Site
            var mainSite = serverManager.Sites[siteName];

            // magic dance!
            // see discussion with David Ebbo http://stackoverflow.com/a/15351473/809357
            var rootVirtualPath = String.Format("/LM/W3SVC/{0}/ROOT/", mainSite.Id); 

            var clientBuildManager = new ClientBuildManager(rootVirtualPath, null);

            clientBuildManager.PrecompileApplication();
        }
    }
}

And this kinda worked. Only not completely. PrecompileApplication() did the job, compiled all the views into ASP.Net Temp folder. Only one issue:

ASP_temp_files

IIS ignores all the pre-compiled files and spins up yet another folder where it runs it’s own compilation. I did confirm that by running IIS into pages that have not been compiled and IIS-used folder did increase it’s size, where as manually created folder is ignored and IIS ignores it.

Maybe I’m doing something wrong with rootVirtualPath, but I did not come with this one by myself, this comes from David Ebbo who has created Razor Generator. And I’d like to think he knows what he is doing. Also in that discussion, people confirmed that this approach worked for them.

So far I’ve failed with this task. I’ll try a few other approaches and will update this as I go.

Reference links:

  1. How do I force compilation of ASP.NET MVC views?
  2. Compile Views in ASP.NET MVC
  3. Azure precompilation doesn’t seem to work
  4. How do I force compilation of ASP.NET MVC views?