Again, continuing to discuss the automatic dynamic page compilation further.
Let's understand how is the page assembly generated with respect to what files are involved. Generating an assembly for a particular .aspx resource is a 2 step process. First, a class file is created that fully represents the markup of the .aspx file. Second, the dynamically generated class is compiled into an assembly and cached in the "Temporary ASP.Net Files" folder. There will be a distinct folder for this for each installed version of ASP.Net. Let's understand this "Temporary ASP.Net Files" folder a bit more:
The Temporary ASP.Net Files folder has one child directory for each application ever executed. The name of the subfolder matches the name of the virtual directory of the application. Pages that run from the Web server's root folder are grouped under the "root" subfolder. Page specific assemblies are actually cached in a subdirectory placed a couple of levels down the virtual directory folder. The names of these two child directories one below the other are the result of a hash algorithm based on some randomized factor along with the application name. A typical path is shown below. The last two directories (numbered names) have fake but realistic names:
\Framework \[version] \Temporary ASP.Net Files \MyWebApp \6789b205 \e40605c7
Regardless of the actual algorithm implemented to determine the folder names, from within an ASP.Net application, the full folder path is retrieved using the following, pretty simple code:
String tempAspNetDir = HttpRuntime.CodegenDir;
So much for the location of the dynamic assembly. How does the ASP.Net runtime determine the assembly name for a particular .aspx page? For each processed page, the application's temporary folder contains a file with the following name:
Obviously, [page] represents the name of the .aspx resource. The xxxxx placeholder is a hash value based on the directory, so all .aspx files from the same folder have the same hash. All page files are compiled in the same folder, regardless of the fact that they might originate from different source folders. The hash value disambiguates files that have the same name but originally belonged to different originating source folders. An internally stored cache of hash values allows the ASP.Net runtime system to quickly identify hash values for any given resource name. The following listing shows the contents of a .compiled file.
<filedep name="/Compilation/Test.aspx" />
<filedep name="/Compilation/Test.aspx.cs" />
Note: The syntax of .compiled files is not documented and might change in future versions of ASP.Net. It is shown here for purely educational purposes and to help to form an idea of how things work under the hood. Be careful to rely on this information for any application.
The <preserve> node contains a reference to the original source file (test.aspx), the name of the class created upon it (ASP.test_aspx), and more importantly, the name of the assembly that contains a compiled version of the class.
The <filedeps> node lists the source files that concur with the definition of the dynamic assembly for the page.
Once the HTTP handler has identified the name of the class that represents the requested page, the .compiled file helps to figure out the assembly name, if any, that contains it. If no .compiled file exists, or if the linked assembly is missing or outdated, the .aspx source is parsed again to create a temporary C# or VB.Net class file. This class file is then compiled to a new assembly whose name is randomly generated.
Note: In ASP.Net 2.0, the name of dynamically created assemblies that represent pages always begin with App_Web_. Other dynamically created assemblies that represent themes, application class files, resources, and global.asax have different names.
Detecting Page Changes:
The dynamically compiled page assembly is cached and used to serve any future request for the page. However, changes made to an .aspx file will automatically invalidate the assembly, which will be recompiled to serve the next request. The link between the assembly and the source .aspx files is kept in the aforementioned .compiled file. Any changes to any of the files listed in the <filedeps> section will also invalidate the assembly. To detect changes, the ASP.Net runtime installs a file monitor infrastructure based on the Win32 file notification system.
When a page is changed, it's recompiled as a single assembly and reloaded. ASP.Net ensures that the next request will be served the new page outfit by the new assembly. Current requests, on the other hand, will continue viewing the old page served by the old assembly. The two assemblies are given different (because they are randomly generated) names and therefore can happily live side by side in the same folder as well as be loaded in the same AppDomain.
That's it for today. Thanks for joining!!! See you tomorrow. Tomorrow see how ASP.Net replaces page assemblies at runtime.
I noticed (with .net reflector) that System.Web.HttpRawUploadedContent.TempFile constructor using impersonation with the internal class ApplicationImpersonationContext.
Why is that?
If I want to read\write from this folder, do I need to do something like that also?
I'm asking from asp.net point of view, I'm mean inside http context.
Great overview. Thanks!
I have an issue where I'm changing constants on referenced dll, but I'm not changing the aspx page. Is there a way to force a single page to recompile? (Besides deleting the .compiled file or compiled dll)