Introduction

When the first request arrives at your web application there is a mind-numbing amount of work to do. The worker process starts, the runtime initializes, ASPX pages are parsed and compiled to intermediate language, methods are just-in-time compiled to native code - and the list goes on and on. If you want to cut out some of the overhead and improve the startup time of your application, then you'll want to look at the precompile features in ASP.NET 2.0.

Although pre-compilation will give our site a performance boost, the difference in speed will only be noticeable during the first request to each folder. Perhaps a more important benefit is the new deployment option made available by the precompile - the option to deploy a site without copying any of the original source code to the server. This includes the code and markup in aspx, ascx, and master files.

In this article we will explore the benefits and caveats around pre-compilation and the new aspnet_compiler tool. There are two modes for pre-compilation: in place pre-compilation and pre-compilation for deployment. We will take a look at in place pre-compilation first.

Background

The compilation process for an ASP.NET Web page has always been divided into two phases. First, the code-behind files and any other supporting classes are compiled into an assembly, and then the individual ASPX files are compiled at runtime. Although this model has many advantages, it has a few drawbacks. ASP.NET 2.0 offers several alternatives to the basic model, providing a wider range of compilation options depending on your specific needs.

Compilation in ASP.NET 1.x

The primary compilation model in ASP.NET 1.x resulted in one application assembly (containing all of the compiled code-behind files and other source code) and one temporary assembly created for each ASPX page that was requested. In some cases, compiler optimizations such as batching may cause the temporary ASPX pages to be compiled into the same assembly. In either case, each ASPX page is compiled into a temporary assembly so that it can be loaded into the ASP.NET runtime.

Although this model has advantages, it also has two primary disadvantages. First, the ASPX pages must be deployed to the Web site in human readable form. If your developers used the code-inline model, that means that some or all of your business logic may also be deployed on a production server. Although IIS and ASP.NET are configured not to expose the raw ASPX pages, a clever attacker could still access the files through any exploit that opened up access to the Web server. Second, the first time anyone requests a Web page, the response will be slower than normal, as the ASP.NET runtime has to compile the ASPX page.

The only control a developer has over this process is whether or not to batch-compile the ASPX pages. In ASP.NET 1.x, you can configure batch-compilation in the web.config file by modifying the <compilation> tag.

Configuring batch-compilation
<compilation
batch="true|false"
batchTimeout="number of seconds"
maxBatchSize="maximum number of pages per batched compilation"
maxBatchGeneratedFileSize="maximum combined size (in KB) of the
generated source file per batched compilation"
</compilation>

Batch-compilation makes a trade, of start-up time for reduced load time, on the first request of a Web page. The other benefit of batch-compilation is that all of the ASPX files are compiled into a single temporary assembly rather than one temporary assembly per page.

Compilation in ASP.NET 2.0

ASP.NET 2.0 offers three different compilation models for a Web application:

  • Normal (ASP.NET 1.x)-In a normal ASP.NET Web application, the code-behind files were compiled into an assembly and stored in the /bin directory. The Web pages (ASPX) were compiled on demand. This model worked well for most Web sites. However, the compilation process made the first request of any ASP.NET page slower than subsequent requests. ASP.NET 2.0 continues to support this model of compilation.
  • Deployment pre-compilation- A new feature of ASP.NET 2.0 allows for full compilation of your project prior to deployment. In the full compilation, all of the code-behind files, ASPX pages, HTML, graphics resources, and other back-end code are compiled into one or more executable assemblies, depending on the size of the application and the compilation settings. The assemblies contain all the compiled code for the Web site and the resource files and configuration files are copied without modification. This compilation method provides for the greatest performance and security at the cost of removing all ability to modify the Web site post-deployment. If you are working with highly visible or highly secure Web sites, this option is the best choice for final deployment. However, if you are building a small site running on your local intranet, and the site changes frequently, full pre-compilation may be over kill.
  • The ASP.NET 2.0 compilation model also allows you to precompile all the code-behind files for your application and still make updates to your code. You can compile your code-behind and original .ASPX files (both partial classes) into a single precompiled class (the page's base class). If you chose to edit your .aspx file at runtime, you must simply recompile your page.
  • Full runtime compilation - At the other extreme of deployment precompilation, ASP.NET 2.0 provides a new mechanism to compile the entire application at runtime. That is, you can put your uncompiled code-behind files and any other associated code in the new \app_code directory and let ASP.NET 2.0 create and maintain references to the assembly that will be generated from these files at runtime. This option provides the greatest flexibility in terms of changing Web site content at the cost of storing uncompiled code on the server.

In Place Pre-compilation

By default, ASP.NET dynamically parses and compiles all the ASPX pages in a folder when the first request arrives for a page inside that folder. ASP.NET also needs to compile applicable files in the special folders, like App_Code, on the first request, and any code-behind files associated with ASPX and ASCX files. The runtime caches all the compilation results in order to quickly process later requests, and does not need to recompile again unless someone edits a file. This behavior gives us a great deal of flexibility, including the flexibility to change code and markup and instantly have the changes reflected in the next browser request.

The price for this flexibility is the performance hit on the first request. Some people have found their ASP.NET applications to be slow starters. These people usually work in the sales department and perform software demos in front of customers. In place pre-compilation makes the "first hit" to a web application and forces all pages and code in the application to compile.

The tool to use for pre-compilation is the aspnet_compiler executable, which you can find in the %WINDIR%\Microsoft.NET\Framework\v2.x.xxxx directory. If we have a web application in the WebSite1 virtual directory under IIS, we could use the following command line to compile the application.

c:\Windows\Microsoft.NET\Framework\v.2.0.50215>aspnet_compiler -v /Website1

The -v parameter specifies that we are passing a virtual path to our web site. On servers with multiple websites you may need to use the -m parameter and specify the full IIS metabase path to the application
(-m /LM/W3SVC/1/Root/WebSite1).

The pre-compiled code will end up inside of the Temporary ASP.NET File directory, just as it would when the runtime compiles files for a browser request. Inside of the bin directory for the compiled site, you'll find the assemblies (dll files). The compiler generates special filenames to avoid naming collisions.

In C:\WINDOWS\Microsoft.NET\Framework\v2.0.50215\Temporary ASP.NET Files, the dll starting with App_Code contains the code from the App_Code directory - not too surprising. Each folder containing aspx, or ascx files will compile into a dll prefixed with App_Web. The files with a .compiled extension contain XML with information about which original source code file maps to which assembly.

With the compiled files in place your web application should have a slightly better startup time, but a primary benefit to in place pre-compilation will be the ability to ensure the web application is error free. If you happen to modify a class or web form and leave an error in the file, the aspnet_compiler will fail and display the compiler error. The tool will also display any warnings, but warning will not stop compilation.

Pre-compilation for Deployment

Pre-compilation for deployment creates an 'executable' (no source code) version of your web application. With pre-compilation for deployment you give the aspnet_compiler the path to your source code, and the path to a target directory for the compilation results, like below.

Aspnet_compiler -p "C:\Dev\WebSite1" -v / C:\Staging

This command will compile the site and place the result in C:\Staging. You must still specify -v as a parameter, even though we are not using a virtual path as either a source or a destination. Instead, the compiler will use this parameter to resolve application root references (~).

The pre-compilation for deployment step will recreate your web site's folder structure in the destination directory. All of the static files (HTML files, image files, configuration files) are copied into the folder structure exactly as they appear in the source folder hierarchy. A bin directory will appear in the target directory with all of the assemblies and .compiled files.

The target directory will contain no source code. All of the classes in the App_Code folder are now compiled into one or more assemblies in the bin directory, and no .cs or .vb files will exist in the target directory. Master page files will also compile to the bin directory and not exist. All the code and markup in ASPX, ASCX, and ASHX files, along with any associated code-behind files, will live inside of one or more assemblies in the bin directory, although these files will still exist in the target directory, they exist as nearly empty 'marker' files. If you open an ASPX file in a pre-compiled target directory you'll see the following content:

This is a marker file generated by the precompilation tool, and should not be deleted!

Note: This behavior is as of beta 2 and may change. The IIS script map for the ASPX file extension leaves the "Verify that file exists" checkbox unchecked, and the site will work without any of the ASPX files present. There is, however, a problem getting IIS to serve a default document for a directory request unless the file is present.

Once the application finishes compiling you can FTP or XCOPY the target directory to a web server (or map a virtual directory to the target directory), and the application will be ready to run. A benefit to pre-compilation for deployment is that no one can make changes to the web application by tweaking the source code - no source code exists! In fact, you can't even place a new ASPX file into the existing application directory structure without causing an error.

Making a change to your site will require you to make a change in the original source code, pre-compile the application again, and redeploy all files to the server. There is one caveat in this scenario, in that pre-compilation generates unique filenames for some assemblies in the bin folder, and these filenames will change each time the pre-compiler executes. The first time you run aspnet_compiler you might see App_Web_lufhs9vn.dll in the bin directory, the next time you might see App_Web_hviqdkt.dll with the same compiled code, even though no source file has changed. This means you might have unneeded dlls in your bin directory if you keep repeatedly copy files to the server without cleanup.

Update: use the -fixednames switch to generate repeatable assembly names.

For some people, the ability to update content on the web server by modifying aspx and ascx files is a needed feature. To support this scenario, the aspnet_compiler has the -u switch for an "updateable" pre-compilation. With updateable pre-compilation the ASPX, ASCX, ASHX, and MASTER files are copied to the target directory in tact - they do not become 'marker' files. You can deploy these files to the server and modify them without causing an error, because the ASP.NET runtime will dynamically parse and compile these files. All of the source code for code-behind files and in the App_Code folder will still be compiled into assemblies and will not need to be deployed.

Miscellaneous

A few other features of the aspnet_compiler include the -d switch, which tells the compiler to generate debugging symbols for the application. Debugging symbols are required if you need line numbers in the stack traces of exceptions. The -f switch will force the compiler to overwrite a target directory, even if a precompiled application already exists there. There are also options to give compiled assemblies a strong name, and allow partially trusted callers.

In addition, you can pre-compile an application directly from Visual Studio 2005 using the Build -> Publish menu item. The Publish Web Site dialog will let you select an http, ftp, or local file system destination as the target.

The precompiler also supports strong naming with the -keyfile, -keycontainer, and -delaysign switches. To allow partially trusted callers, use the -aptca switch.

Conclusion

Although performance is often touted as a benefit of pre-compilation, I tend to think of pre-compilation as a safety feature. Safety in knowing all of the code in a web application compiles without an error, and safety in keeping your source code out of a shared host environment.