13

I'm working on a website that I'd like to use in-place compilation on in order to make the first hit faster. I'd like to use the ClientBuildManager.CompileFile method to do the in-place compilation so that I have control of the compiling process. For a variety of reasons, this is the ideal way to compile this website.

Why Does IIS Build to a Different Subdirectory under "Temporary ASP.NET Files"?

When I compile a website file by file via ClientBuildManager.CompileFile method in an exe built for this purpose, the output goes to a Subdirectory under "Temporary ASP.NET Files". However, when the website is hit later, IIS rebuilds the controls under a different subdirectory under "Temporary ASP.NET Files" rendering the previous in-place compilation worthless.

Note: The assemblies created during in-place compilation under "Temporary ASP.NET Files" are left alone (still exist).

Note: Both the in-place compilation assemblies folder and IIS generated assemblies folder are under the same "Temporary ASP.NET Files" dir.

Example:

  • C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\root\2ba591b9\[in-place compilation folder name]
  • C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\root\2ba591b9\[IIS generated assemblies for website]\

ClientBuildManager.CompileFile Configuration

var buildParameter = new ClientBuildManagerParameter
   {
      PrecompilationFlags = PrecompilationFlags.Default,
   };
var clientBuildManager = new ClientBuildManager(
   RootVirtualPath, RootPhysicalPath, null, buildParameter);
...
clientBuildManager.CompileFile(relativeVirtualPath, callback);

Where RootVirtualPath is simply "" for the default website. RootPhysicalPath points to the location on disk of the website. relativeVirtualPath is of the form "~/myFile.aspx". The callback is used to track progress.

Sam
  • 26,946
  • 12
  • 75
  • 101
  • I would guess that with this code, they would not end up in the same folder even if you called `PrecompileApplication` instead of `CompileFile`. It's been a while since I looked at this stuff, so I may need to dig in the sources. – David Ebbo Mar 12 '13 at 00:12

2 Answers2

12

I think that what you're seeing is actually unrelated to the use of CompileFile vs PrecompileApplication. i.e. if you were to do the same thing but call PrecompileApplication(), you still would get a folder mismatch.

Note that technically, you are not creating the CBM object correctly. The correct way of calling it is to rely on IIS information to locate the files. To do this:

  • Pass something like /LM/W3SVC/7/ROOT/ for appVirtualDir
  • Pass null for appPhysicalSourceDir

Note that the '7' is just an example. To get the correct number:

  • Run inetmgr
  • Go to the advanced settings for the site
  • Find the site ID. That's the number you want in /LM/W3SVC/ID/ROOT/

I'm explaining this for the record, because unfortunately, I was not able to get the folders to match even in this way. It's possible that this scenario is just broken in ASP.NET (it used to work!).

An alternate possibility is to do it server side. e.g.

  • include a page in your site that you'll use to trigger selective precompilation.
  • In there, call BuildManager.GetCompiledType("~/myfile.aspx"), and similar calls for each page (or user control, etc) that you want to precompile.
  • When you want to trigger your custom precompilation, just request that page

Of course, there is also the low tech alternative of simply requesting the pages you want compiled ahead of time to warm up your site.

David Ebbo
  • 42,443
  • 8
  • 103
  • 117
  • +1. Thanks for the response. So did you have something similar working at one point in time? - you mentioned that it used to work, so you got me curious. I'll try out having a page on the site do the compilation to see if it makes a difference as well. – Sam Mar 12 '13 at 02:18
  • I worked on this code back in the ASP.NET 2.0 time frame, and I know it was working then, but that was a long time ago (close to 10 years!). It's possible that it became broken over time, in particular when we moved from IIS6 to IIS7 :( My guess is that not many people have been trying this in-place compilation lately. But I think the runtime based approach will work. – David Ebbo Mar 12 '13 at 06:11
  • +200 bounty. Thanks for the response. Your answer is deserving of the bounty. I opened a ticket with Microsoft about this, so we'll see what happens. – Sam Mar 18 '13 at 23:49
  • Thanks! Is there a ticket # for reference? Maybe I can ask some people who work on this code today. – David Ebbo Mar 19 '13 at 03:25
  • I asked the ticket owner to contact you. – Sam Mar 19 '13 at 15:19
  • 1
    You were right. Adding the IIS path of `"/LM/W3SVC/[SITE ID]/ROOT/"` as the `appVirtualDir` and then passing `null` for `appPhysicalSourceDir` did the trick. Note that you must pass `null` for `appPhysicalSourceDir` for it to work. Thank you. You had the answer all along. I think I didn't read it carefully enough the first time. – Sam Mar 20 '13 at 17:27
  • I think where I caused confusion is when I said that it didn't actually work for me when I tried that. And I'm not really sure why it didn't, but I'm glad that it does for you. It also worked for the Microsoft support technician (Bret), so I probably did something wrong :) – David Ebbo Mar 20 '13 at 18:23
  • 1
    Does this method work for recompiling views that have changed? I have a plugin architecture on my MVC ASP.NET app, because the view are compiled when requested, when removing and reinstalling the plugin views I get a missing assembly / namespace exception. I'm trying to force the server to recompile the views within the new plugin dir. Any pointers? thx – sambomartin Nov 17 '14 at 10:37
  • 1
    FWIW, when I experienced this same issue using `aspnet_compiler.exe -m`, I had to specify the path as `/LM/W3SVC//ROOT`, **without the trailing slash**. Otherwise, the compile output would be placed into the wrong directory. – Jeff Sharp Feb 26 '16 at 15:44
3

Sam, the answer is a cross between David Ebbo's answer, and your original code.

var buildParameter = new ClientBuildManagerParameter
   {
      PrecompilationFlags = PrecompilationFlags.Default,
   };
var clientBuildManager = new ClientBuildManager(
   RootVirtualPath, RootPhysicalPath, null, buildParameter);
...
clientBuildManager.CompileFile(relativeVirtualPath, callback);

If you do what David Ebbo said and use this for the RootVirtualPath when constructing the ClientBuildManager:

/LM/W3SVC/7/ROOT/

you then have to pass in null for the RootPhysicalPath.

That should get rid of the rest of the issues, and it should build to the same directory that IIS is looking for.

Silas
  • 406
  • 2
  • 11