Since its first version, ASP.NET has made a point of compiling pages as opposed to the interpreted pages common to classic ASP. In ASP.NET 2.0, side by side with site precompilation you also find an apparently baffling feature named no-compile pages. Simply put, a no-compile page is something in between classic ASP.NET pages and HTML pages. In the course of this article, I’ll review tools and techniques for implementing site precompilation, and also take an in-depth look at no-compile pages and the scenarios where they apply.
The ASP.NET compiler toolPrecompilation in ASP.NET 2.0 is accomplished through a system tool named the ASP.NET compiler. The executable is aspnet_compiler and is located in the ASP.NET installation folder on the web server machine:
%WINDOWS%\microsoft.net\framework\ v2.0.50727The ASP.NET compiler supports all forms of precompilation, and is implicitly used by Visual Studio 2005 when you choose to publish a web site. I normally use the tool from the command line for in-place precompilation, but take advantage of all Visual Studio 2005’s facilities for deploy precompilation. The compiler’s command line is fairly rich and complex. The table below contains the full list of switches and their descriptions:
Table 1: The list of recognized switches for aspnet_compiler.exe
|-aptca||If specified, compiled assemblies will allow partially trusted callers.|
|-c||If specified, the precompiled application is fully rebuilt.|
|-d||If specified, the debug information is emitted during compilation.|
|-delaysign||If specified, compiled assemblies are not fully signed when created.|
|-errorstack||Shows extra debugging information.|
|-m||Indicates the full IIS metabase path of the application.|
|-f||Indicates that the target directory will be overwritten if it already exists and existing contents are lost.|
|-fixednames||If specified, the compiled assemblies will be given fixed names.|
|-keycontainer||Indicates the name of the key container for strong names.|
|-keyfile||Indicates the physical path to the key file for strong names.|
|-p||Indicates the physical path of the application to be compiled. If missing, the IIS metabase is used to locate the application. This switch must be combined with -v.|
|-u||If specified, indicates that the precompiled application is updatable.|
|-v||Indicates the virtual path of the application to be compiled. If no virtual path is specified, the application is assumed to be in the default site: W3SVC/1/Root.|
For a better understanding, however, let’s break it into four main pieces, as below.
aspnet_compiler [source] [target] [key] [error]The [source] block includes the switches (–m, –v, –p) by means of which you specify the location of the files in the web site you’re going to precompile. If you use the full IIS metabase path to indicate the source application (–m switch), you just ignore the other two. The full IIS metabase path is usually something like:
/LM/W3SVC/1/Root/ [Name of the Web application]If you only know the virtual path, you go with the –v switch. The –p switch can only be combined with –v and indicates the physical path behind the specified virtual path. If no –p switch is specified, IIS metabase is used to locate the application.
The [target] block identifies the destination of the precompiled files and the options to apply. In the target block you indicate the destination of the precompiled files, and optionally use any of the following switches: –c, –u, –f, –d, –fixednames. If no target directory is specified, the application is precompiled in-place. The –c switch indicates that the application will be built from scratch as it is compiled for the first time; all pages are compiled even if they look unchanged. The –d switch forces the ASP.NET compiler to emit debug information in the final assemblies.
The [key] block gathers settings and information to create strongly-named page assemblies. To create a strongly-named assembly you need to specify a key file generated with the Strong Name tool. You find the Strong Name (sn.exe) tool in the following folder:
%DRIVE%\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BinOf particular interest is the –aptca switch. If set, the strongly-named page assemblies will accept calls from partially trusted caller components. A partially trusted caller is any component that “appears” not to be fully trusted – such as a component run from a network share. By default, strongly-named assemblies protect every public and protected method of every publicly visible class with a LinkDemand attribute for fully trusted assemblies. By using the –aptca switch you require that page assemblies, even if strongly-named, don’t include the AllowPartiallyTrustedCallersAttribute (familiarly, APTCA) attribute on their public and protected class members. For more information on APTCA, take a look at blogs.msdn.com/shawnfa/archive/2005/02/04/367390.aspx.
The [error] block contains the sole –errorstack switch. If the switch is set, the ASP.NET compiler will include stack trace information if it fails to compile any page in the application.
Finally, note that the ASP.NET compiler requires localhost access and administrative rights on the web server machine to operate.
Now that you have a good grasp of the underlying machinery, let’s see how to actually precompile a web site. I’ll attack with the simplest case – in-place precompilation.
In-place precompilationBoth in-place and deploy precompilation bring some benefits to the development team, and both are great to have available. However, it won’t take long to realize that in-place and deploy precompilation have significantly different value in the economy of a web application. Development and support teams will be mostly interested in deploy precompilation, whereas the sales team find the net effect of in-place precompilation – no load delay – extremely appealing and helpful for their demos.
In-place precompilation comprises the automatic compilation of all pages in the site and is done just before the site goes live. The attribute “in-place” indicates that the precompilation occurs on the web server machine on an already and successfully deployed web site. The ASP.NET compiler recursively scans the site and silently invokes any pages simulating a client user. In doing so, any required page assemblies are created and placed in the temporary ASP.NET folder. The temporary ASP.NET folder is the server folder where the ASP.NET runtime stores any dynamically created page assemblies. The physical path of the folder is shown below:
%WINDOWS%\ microsoft.net\ framework\ v2.0.50727\ Temporary ASP.NET Files\ [Application Name]Having any required page assemblies ready speeds up the loading of each requested page. The ASP.NET runtime, therefore, has no need to spend time and CPU cycles to parse the ASPX source file to a class and then compile. The command line required for in-place precompilation is:
aspnet_compiler –v /YourAppNameAs mentioned, you run the compiler on the web server machine equipped with IIS after the application has been fully deployed. The .NET Framework must be installed, and compilers for the language used by the pages must be available. (This is not usually an issue as long as you use any of the native .NET languages such as Visual Basic .NET, C#, J#, or perhaps Managed C++.)
In-place precompilation doesn’t alter in any way the structure of the site, and still allows the site to be extended with new pages or newer versions of existing pages. If a new page is added, or if an existing page is modified at some point, the first user hitting that page will experience the notorious first-hit delay. Precompilation takes a relatively short time, although the notion of “short” time must be measured against the size of the application and the type of service it provides. In general, stopping the site to apply changes and precompile is not a great idea, even though the stop is typically estimated in a few minutes. So, while precompiling before going live has its own benefits, I would not suggest precompiling the site after applying a bunch of changes. All considered, the first-hit delay is not a big deal and is a small overhead that only one user pays. Note, though, that when you precompile a site only new or modified files are actually processed. Up-to-date files are just skipped.
The greatest benefit you get out of in-place precompilation is cross-checking the site to find out any errors that may have been left. Precompilation, in fact, will fail on the application if any page fails. If in-place precompilation is successful, you can be sure that your application has no broken pages. In-place precompilation, of course, simply checks the syntax of pages; it says nothing about logical bugs.
Deploy precompilationThe goal of deploy precompilation is transforming the ASP.NET application into a closed executable that preserves intellectual property by hiding the page markup and the source of code-behind classes. Deploy precompilation generates a representation of the site made of assemblies, static helper files, and configuration files that can later be zipped or packaged into a MSI file for actual deployment. As you can see, deploy precompilation also includes the beneficial side effect of in-place precompilation – the elimination of first-hit delay.
You can precompile a site for deployment using either Visual Studio 2005 or the ASP.NET command line compiler. Figure 1 shows the dialog box you face in Visual Studio 2005 when you click the Build|Publish web site menu item.
Figure 1: Precompiling a site for deployment in Visual Studio 2005
In the text box, you type the local or remote location where the compiler will save files. Personally, I tend to use a local path in such a way that I can later save everything to an MSI package for manual deployment. This is a reasonably common scenario for sites hosted by Internet service providers. If you have direct access to the web server machine, you can indicate the path using an FTP or HTTP address.
To force the command line tool to precompile a site for deployment, you use the following syntax:
aspnet_compiler -v /ProAspNet20 c:\Precompiled\ProAspNet20In its simplest form, it is the same syntax as for in-place precompilation, except for an additional parameter – the target location. Figure 2 shows the output of a precompiled site.
Figure 2: A web site precompiled for deployment on the local development machine
As you can see, a new configuration file, PrecompiledApp.config, shows up in the target location. The file is a marker and informs the ASP.NET runtime that the application is precompiled. Here are the standard contents of the file:
<precompiledApp version=”2” updatable=”false”/>The precompilation process doesn’t touch internal or static files such as web.config, web.sitemap, images, and HTML pages. All these files are just copied to the target destination. The overall structure of the site is intact; the same can’t be said for the contents of some files. For example, the original markup contents of any .aspx file have been stripped off and replaced with a string of text. The text is a mere message that warns you not to remove .aspx files even though they don’t appear to contain any significant text.
The Bin folder of a precompiled application contains a different set of assemblies. In addition to any assemblies explicitly referenced in the original project, the folder now contains a bunch of page assemblies. By default, the compiler combines together multiple source files into a single assembly according to the type of file and file dependencies. Assemblies are given random generated names which change each time you recompile the site for updates. As you can guess, it’s challenging for anybody to replace a single page in a site precompiled for deployment. The –fixednames switch helps to solve the issue only partially.
When set, the –fixednames option forces the compiler to name page assemblies after the page name. More importantly, though, the option forces the compiler to generate exactly one assembly per page. There are two more caveats. First, the name of the assembly is still a bit cryptic as it appends a random number to the name of the original page file. Second, in this way the number of assemblies to deploy can easily grow as high as a few hundred. For this reason, the ASP.NET team released a new utility (not included in the original ASP.NET 2.0 setup) named aspnet_merge.exe.
Merging page assembliesThe aspnet_merge tool enables you to combine all assemblies generated by the precompilation process into a smaller number of deployable assemblies. There are three options:
- Combine all ASP.NET generated assemblies (global.asax, resources, App_Code, themes) into a single named assembly
- Combine all page assemblies into a single named assembly
- Combine all page assemblies into a single named assembly for each folder in the web site
Once installed, a new option shows up in the web site menu of Visual Studio 2005 to let you add a new deployment project to your existing ASP.NET project. Figure 3 shows the resulting dialog box, which is just a nice GUI built around the merge tool.
Figure 3: The property page dialog of a Web Deployment Project
Web deployment has been available for quite some time since the official release of Visual Studio 2005 and the .NET Framework. A newer update is discussed at weblogs.asp.net/scottgu/archive/2006/03/27/441147.aspx and should be available for download by the time you read this.
One possible aspect of the merge tool can trip you up. Specifically, when you merge together multiple pages belonging to distinct folders into a single named assembly, you must guarantee that code-behind class names are unique. If you add the page through Visual Studio 2005, the path to the page is reflected in the class name but the default namespace of the class is always ASP. If you change the class name, you must make sure that no duplicate class names exist even in other folders. One way to ensure this is using custom namespaces.
Updatable precompilationSites packaged for deployment only are not sensitive to file changes. When a change to any of the pages is required, you modify the original page, recompile the whole site, and redeploy the new layout. The only exception is the site configuration; you can update web.config or web.sitemap on the production server without having to recompile the site.
What if, given the characteristics of the site, you are going to update it quite frequently and still want to leave no source code around the server? In theory, for each change to the known structure of the site you should recompile the whole site to a new executable image, package that image to a deployable file and update the production server. As mentioned, the whole site must be recompiled even if a single page changes or if you add a new page. The disadvantage lies in the fact that by default the compiler tool generates random names for each created assembly. By using the –fixednames attribute, you force it to generate repeatable assembly names. If the frequency of updates is too high, you should consider a second variation of deploy precompilation: updatable precompilation.
By adding a –u switch to the compiler tool (or just checking a checkbox in the Visual Studio 2005 user interface), you precompile a site to be updatable. What’s the difference? In this case, the CodeFile attribute is removed from any .aspx files and the Inherits attribute is modified to point to the dynamically created page assembly. For example, consider the following .aspx heading:
<%@ page language=”C#” codefile=”GenericError.aspx.cs” inherits=”GenericError” %>After updateable precompilation, it becomes as follows:
<%@ page language=”C#” inherits=”GenericError, App_Web_genericerror.aspx.cdcab7d2” %>Other files are compiled as usual.
The updateable precompilation allows you to make limited changes to the ASP.NET pages after compiling them. For example, you can change the position of controls or settings regarding colours, fonts, and other visual parameters. You can also add new controls to existing pages, as long as they do not require event handlers or other code. Code files are not deployed as source code, so you can’t update the logic of the page without recompiling and deploying the layout again.
It is worth noticing that updatable precompilation in ASP.NET 2.0 is nearly identical to the compilation and deployment model of ASP.NET 1.1, where .aspx files are deployed as source files and all code-behind classes are compiled to assemblies.
No-compile pagesIn ASP.NET 2.0, the compilation model has been significantly refactored and extended. Site precompilation is perhaps the most popular and loudly requested of the new features. Another quite interesting feature is no-compile pages. They are special pages that just never get compiled. So what’s the ultimate purpose of no-compile pages, and what’s the difference between them and static HTML pages?
To start off, you create a no-compile page by setting the CompilationMode attribute on the @Page directive to Never. When a no-compile page is requested, no page assembly is created and persisted to disk. Instead, an instance of the page builder component is cached in memory and used to create the page output for each and every request. The page builder is a special component that supports the page parser in building the page control tree. When compilation is turned on, the control tree is used to obtain a class to compile. When compilation is off, the control tree is used to obtain markup. Needless to say, classes are necessary if you want to give programmers the power of attaching their own code to the page. No-compile pages are made of server controls and literals but contain no code at all.
No-compile pages are not for every application. They are exclusively designed for improving the scalability on very large web sites with thousands of pages. No-compile pages can’t be bound to a code file and can’t contain a server-side <script> block. The only executable piece of code allowed in a no-compile page are $-expressions. There are two main benefits out of no-compile pages. In a secure environment like SharePoint, no-compile pages prevent developers from writing potentially buggy code that can cause problems to the hosting environment and even tear it down. In a large content-based web site, no-compile pages avoid the need to compile thousands of pages.
SummaryWhat’s the purpose of site precompilation? As it turns out, improved performance is often brought as an evidence of the benefits of precompilation. While precompilation certainly saves users from the notorious first-hit delay, I don’t believe that this simple fact, which affects only one user per page, is going to change users’ feelings about the overall usability of the site. Precompilation is mostly a safety measure: using precompilation makes your application safer because when you deploy it you know that all pages compile without errors. In addition, your precompiled application is safer because any valuable source code is out of reach.
To get both benefits you must opt for deploy precompilation. This form of precompilation creates a read-only image of the site that might be packaged to MSI or ZIP files as well as directly x-copied to the web server machine. For web sites that are subject to frequent page updates, updateable deploy precompilation is probably better because updateable precompilation allows you to edit and add new pages. These new pages will be compiled on first hit, though.
Dino Esposito is a Solid Quality Learning mentor and prolific author. He is the author of the two volumes Programming Microsoft ASP.NET 2.0 (Microsoft Press, 2005), writes the Cutting Edge column for MSDN Magazine, and regularly contributes to a variety of developer magazines. He is also a regular speaker at Bearpark’s annual DevWeek conference – the 10th annual event is scheduled for 26 February–2 March 2007.