PageParser.GetCompiledPageInstance Weirdness When Debug Set To False In Web.config

This is a story of intrigue.

Ok, perhaps that is a bit overblown. This is really a story of schizophrenia. It is the story of a method PageParser.GetCompiledPageInstance that exhibits a different behavior depending on whether or not you have the <compilation> tag’s debug attribute set to true or false.

The problem first came up when deploying the most recent builds of Subtext with this attribute set to false. This was the natural response to Scott Guthrie’s admonishment, Don’t Run Production ASP.NET Applications with debug="true" enabled..

However, this affected Subtext in an unusual manner. Subtext employs an URL rewriting mechanism I wrote about before. It relies on the using an IHttpHandler that is created by calling PageParser.GetCompiledPageInstance.

I will spare you all the details and cut to the chase. GetCompiledPageInstance takes in three parameters:

  • virtualPath (string)
  • inputFile (string)
  • context (HttpContext).

In the initial request to the Subtext root, the values for those parameters on my local machine are:

  • virtualPath = "http://localhost/Subtext.Web/Default.aspx"
  • inputFile = "c:\projects\Subtext.Web\DTP.aspx"
  • context = (the current context passed in by the ASP.NET runtime)

The interesting thing to note is that there is an actual aspx file named Default.aspx located at http://localhost/Subtext.Web/Default.aspx. When the debug compilation option was set to true, this method would return a compiled instance of DTP.aspx (hence the URL rewriting).

But when I set debug="false", it would return a compiled instance of Default.aspx. Holy moly!

I confirmed this by attaching a debugger and going through the process multiple times. Using Reflector, I started walking through the code for GetCompiledPageInstance until my eyes started to burst. There is a lot of machinery at work under the hood. I eventually found some code that appears to generate a URL path differently based on debugging options. Not sure if this was the culprit, but it is possible.

Setting debug="false" causes the runtime to perform a batch compilation. Thus a request for /Default.aspx is going to compile all *.aspx files in that folder into a single DLL. Setting that debug value to true causes ASP.NET to compile every page into its own assembly.

My fix is a bit of a hack, until I can get a deeper understanding of what is really happening. As I see it, calling GetCompiledPageInstance with a virtualPath that points to a one file while passing in a different physical file path to inputFile is causing some confusion. Perhaps due to the batch compilation.

To remedy this, I simply have a check before we call GetCompiledPageInstance to check the end of the virtualPath for /Default.aspx (case insensitive of course). If it finds that string, it truncates the default.aspx portion of it. That seems to do the trick for now since this is pretty much the one place in which URL rewriting would attempt to rewrite a url that itself points to a real page.

For a nice look under the hood regarding the compilation option, check out this post by Milan Negovan.

Please keep in mind that this is a separate issue from deploying your compiled assemblies in debug mode or with debug symbols. This has to do with the ASP.NET runtime compiling the ASPX files at runtime.

What others have said

Requesting Gravatar... Steve Harman May 03, 2006 11:14 AM
# re: PageParser.GetCompiledPageInstance Weirdness When Debug Set To False In Web.config
Excellent post Phil... and the article you linked to reguarding deployment of production code with the compilation flag is most certainly worth a read!

One question,
Did you get a chance to test your hack in a subText setup where there is an application configured? For example in my install, subText physically lives at the root of my site but has an application ("blog") configured. In this case I've had to actually create a physical folder named "blog" off of the site root and it contains only one file (named Default.aspx). This allows me to make a request for stevenharman.net/blog/ and the URL rewriting works correctly.

So, I just one to make sure that a request for stevenharman.net/blog/Default.aspx will get correctly routed thru the URL rewriting framework as there is actually a file at that location. From the sound of your post, it will... but want to be sure.

Again, great work Phil!
Requesting Gravatar... Haacked May 03, 2006 11:38 AM
# re: PageParser.GetCompiledPageInstance Weirdness When Debug Set To False In Web.config
Steve, I didn't test case specifically. I should have.

By the way, instead of configuring a subfolder named "blog", you could use our 404 handling to take care of it.

Simply delete the "blog" folder (or delete default.aspx in there) and in IIS map the 404 error to /SystemMessages/FileNotFound.aspx.

This makes it so you don't have to create the physical subfolder for a blog with an "application" (though I now prefer the term "subfolder" over "application" despite the fact that the column name in the DB is "application".)
Requesting Gravatar... Steve Harman May 03, 2006 12:53 PM
# re: PageParser.GetCompiledPageInstance Weirdness When Debug Set To False In Web.config
By the way, instead of configuring a subfolder named "blog", you could use our 404 handling to take care of it.
Oh yeah! I did configure that the last time I deployed, but I forgot that it meant I no longer need the "subfolder for each blog" hack. I'll go ahead and remove the subfolder when I get home.

Good catch!
Requesting Gravatar... Adam Kinney May 06, 2006 11:17 AM
# re: PageParser.GetCompiledPageInstance Weirdness When Debug Set To False In Web.config
Interesting issue, I all I can say is move to virtual path provider when you can. It's been smooth sailing for us since we switched.
Requesting Gravatar... Innocent Bystander Aug 07, 2007 4:55 PM
# re: PageParser.GetCompiledPageInstance Weirdness When Debug Set To False In Web.config
Aren't your virtualPath and inputFile supposed to point to the SAME file? Both should either end in default.aspx or DTP.aspx.

I'm not sure why you are passing conflicting information to the method.

Example:

virtualPath = "http://localhost/Subtext.Web/Default.aspx"
inputFile = context.Server.MapPath(virtualPath);

context.RewritePath(virtualPath, String.Empty, String.Empty);

return PageParser.GetCompiledPageInstance(virtualPath, inputFile, context);
Requesting Gravatar... Haacked Aug 08, 2007 9:17 AM
# re: PageParser.GetCompiledPageInstance Weirdness When Debug Set To False In Web.config
@Innocent Not necessarily. We're doing URL rewriting here, so the requested URL (and the one that should be represented when testing for the virtual URL) is /default.aspx, but the actual file that handles that URL is DTP.aspx.

What do you have to say?

(will show your gravatar)
Please add 5 and 1 and type the answer here: