Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ASP.NET 1.1 Web Application Compilation and Pre-compilation

0.00/5 (No votes)
7 Sep 2005 1  
Describes how to pre-compile or compile .aspx and .ascx files of C# or VB ASP.NET 1.1 web application; includes full source code, compilation library, runtime library and a demo project.

Please use the new version of the source code that is available in ASP.NET 1.1 Web Application Compilation and Pre-compilation - Enhanced article.

Introduction

If you ever wanted to pre-compile (i.e. make ASP.NET runtime to compile all pages and user controls within a single HTTP request) an ASP.NET 1.1 web based application or compile all .ASPX pages and .ASCX user controls into an assembly - here is a solution that allows you to:

  • Pre-compile all .ASPX and .ASCX pages and user controls in an application without making actual HTTP requests to them (i.e. pages will be compiled but not executed).
  • Generate source code for all .ASPX, .ASCX files and compile them into assembly; it also allows you to embed any external files (style sheets, JavaScript files, images, etc.) into assembly as Win32 resources.

If you choose to compile you will need to deploy:

  • Global.asax
  • Web.config
  • Assemblies at bin directory.
  • External files (.css, .js, .gif, etc.) if not embedded.

If the application uses multiple web.config files, you will need to deploy them at the corresponding directories. In order that IIS redirect requests made to a directory (for example http://localhost) to the default document (http://localhost/Default.aspx) you will need to deploy empty Default.aspx files in all directories where that is required. The reason is that when a request to a directory is made IIS searches the directory for a default document (Index.html, Default.aspx, etc.) and if not found returns an error or the directory's content. If you put Default.aspx in the directory, IIS will forward the request to ASP.NET runtime.

You will not need to deploy any additional .aspx or .ascx files unless there are files that are not compiled - i.e. you can combine compiled and not compiled pages. If a page or user control is compiled, no .aspx or .ascx file is required in order to run. If you choose not to compile some of the pages and/or user controls, you will need to deploy them.

Requests to all embedded external files are served as if made to a normal file. In order to make IIS forward such requests to ASP.NET runtime you will need to map the extensions of these files' names to aspnet_isapi.dll.

Application

The solution has been tested on C# and VB ASP.NET 1.1 projects. It is possible that it will work with other programming languages as well, as it does not utilize language dependent features.

Note to VB developers: There is an issue with VB projects because the VB compiler does not remove unused assembly references as the C# compiler does. That is if an assembly reference is passed to the compiler and that assembly is not actually used by the code the compiler will still add reference to the assembly in the compiled assembly. Because of that, references automatically generated by ASP.NET runtime assemblies may be added to the assembly when compiling an application (.aspx and .ascx files). To prevent that, such references are removed during source code generation but this may also remove some valid references, which will prevent compilation. To avoid this, your project should not use assemblies with eight characters long names that contain only lower case letters and/or digits. For further information, see the WebApp.Web.Compilation.Compilation.IsAutoGeneratedAssembly method.

How pre-compilation works

To make ASP.NET runtime compile a page without making a HTTP request to it one needs to invoke the pubic static System.Web.UI.PageParser.GetCompiledPageInstance(String, String, HttpContext) method in the System.Web assembly. The parameters are the virtual path to the page, physical path to the page and HttpContext. For example, you can use the following code to pre-compile a page:

System.Web.UI.PageParser.GetCompiledPageInstance("~/MyPage.aspx", 
                               MapPath( "~/MyPage.aspx" ), context)

This method first checks if the page is compiled and the content of the .aspx file is not changed and if not so parses the page and compiles the generated code. During the parse phase if the page directly uses any user controls, they are parsed and compiled as well.

Pre-compilation of user controls is only needed if there are controls that are not directly used in a page but loaded by the System.Web.UI.TemplateControl.LoadControl(String) or System.Web.UI.TemplateControl.LoadTemplate(String) methods. Otherwise, all the user controls should be compiled by ASP.NET runtime during pre-compilation of pages. To cause the ASP.NET runtime to compile a user control one should invoke the System.Web.UI.UserControlParser.GetCompiledUserControlType(String, String, HttpContext) method. The parameters are the same as that of GetCompiledPageInstance but the method is internal static as well as System.Web.UI.UserControlParser type so it should be invoked by using reflection.

How to pre-compile an application

The source code includes a page named "Compilation.aspx". It is a standalone page that does require its code-behind to be compiled in order to run (has "src" attribute applied in its @Page directive). To pre-compile an application:

  1. Compile the code-behind of the application (if this is required).
  2. Put the WebApp.Web.Compilation and WebApp.Web.Compilation.Runtime assemblies (comes with the source code) in the bin directory of the application.
  3. Put "Compilation.aspx" and "Compilation.aspx.cs" in the application's root directory.
  4. Use "Compilation.aspx" to pre-compile all .aspx and .ascx files.

Compile .aspx and .ascx files into assembly

The solution allows you to generate source code for all or some of the .aspx and .ascx files in an application and compile them into assembly. It also allows you to embed any external files your application needs (.css, .js, .gif, etc.) into same or another assembly and to use these files in the same way as if they were external.

The solution consists of two C# projects:

  • WebApp.Web.Compilation - includes types that allow you to pre-compile, compile and embed resources; requires WebApp.Web.Compilation.Runtime.
  • WebApp.Web.Compilation.Runtime - includes types required to run a compiled application (HTTP handlers, resource manager).

Compilation process consists of the following steps:

  1. Code-behind compilation
  2. Code generation for all or some of the .aspx and .ascx files.
  3. Addition of external resources (if you wish).
  4. Compilation of generated code and resources into assembly.

Code-behind compilation

One must compile the code-behind of its project in order that the .aspx and .ascx files can be parsed. If there is the src attribute applied in pages they should be out of the compilation process.

If you use src attributes for some of the pages or user controls their .aspx or .ascx files cannot be compiled because you cannot compile the generated source code - this requires that the base types' source code (code-behind) also participates in the compilation or is already compiled in an assembly.

Code generation for all or some of the .aspx and .ascx files

This and the following steps can be done by using Compilation.aspx or by developing custom code, which utilizes the WebApp.Web.Compilation assembly. Compilation.aspx allows you to specify assembly name, location where to save the generated source code and resource files to embed. However, Compilation.aspx generates code for all .aspx and .ascx files in an application's root directory and its sub-directories so if one wishes to customize the process she will need to alter the page or write its own code.

If you just want to compile your application - you can use Compilation.aspx and jump directly to the "Compiled application at runtime" section. Just follow the steps in "How to pre-compile an application". The only recommendation is to pre-compile your application first so that you can be aware of any parse errors and fix them prior to compilation. Compilation.aspx displays the detailed output for all errors found during pre-compilation or compilation.

For others who need more details or customized compilation:

The heart of the compilation process is the WebApp.Web.Compilation.Compilation.GenerateCode(HttpContext,string) method. It allows you to generate code for any .aspx, .ascx or .asax file that contains no parse errors. Generating code for Global.asax is possible but may be not usable as there is no ordinary way to handle such requests.

WebApp.Web.Compilation.GenerateCode can be executed only in active HTTP context - that is when a request is made to the application. That is because the method utilizes ASP.NET runtime to parse the file and obtains the generated code by using reflection.

The method creates instances of one of the parsers supplied by the .NET Framework: System.Web.UI.PageParser, System.Web.UI.UserControlParser, System.Web.UI.ApplicationFileParser and their corresponding compilation classes (which actually generates code by using a supplied instance of the parser): System.Web.Compilation.PageCompiler, System.Web.Compilation.UserControlCompiler, System.Web.Compilation.ApplicationFileCompiler. All types are internal (except System.Web.UI.PageParser ) and all have internal constructors so instances are created by using reflection. To go around the standard initialization and utilization of these types reflection is used to get or set values of internal/private properties and fields directly. As a result, the generated code is obtained but the page or user control is not actually compiled - i.e. no assembly is generated (as if when the control is parsed normally by ASP.NET runtime). That excludes the case when a page or user control uses other user controls directly. In such a case, these other controls are compiled during the parsing process of the control but this does not matter.

After the generated code is obtained (System.CodeDom.CodeCompileUnit instance) the following issues must be taken care of:

  • Renaming the generated type

    That is required as ASP.NET runtime derives the type name from the name of the parsed file - that is if you have two files with the same name in different directories the types generated for them will have the same names. For that reason you cannot use two different user controls with the same name in an .aspx page - a compilation error occurs when ASP.NET runtime tries to compile the page. The new name of the type is derived from the relative physical path of the page or user control (based on the application's root directory). One can customize that by modifying the WebApp.Web.Compilation.Runtime.CompilationUtils.GetTypeName method that generates namespace and type name by the specified physical path. WebApp.Web.Compilation.Runtime.CompilationUtils.GetTypeName is also used to obtain the name of the type that corresponds to the specified virtual path at runtime (when a request is made to a page or user control).

  • Renaming all usages of the generated type's name

    This is done by enumerating the methods and expressions in the generated type and fixing them. The patterns where to find these usages can be determined by examination once the source code is generated.

  • Fixing the type names of all user controls used

    This step is required because of the following two reasons:

    • User control instances are not obtained by using the System.Web.UI.TemplateControl.LoadControl(String) method. Instead, instances are directly created and initialized:
      private ASP.WebUserControl1_ascx __control3;
      
      // ...
      
      private System.Web.UI.Control __BuildControl__control3()
      {
          ASP.WebUserControl1_ascx __ctrl;
          
          #line 19 "..."
          __ctrl = new ASP.WebUserControl1_ascx();
      
          // ...
      }
      
      public void __DataBindcontrol__control3(object sender, 
                                           System.EventArgs e)
      {
          System.Web.UI.Control Container;
          ASP.WebUserControl1_ascx target;
          
          #line 96 "..."
          target = ((ASP.WebUserControl1_ascx)(sender));
      
          // ...
      }
    • For all user controls used directly by a page or a user control, the source code need to be generated as well. Otherwise, the source code of the page or user control cannot be compiled (as some of the types it requires will not exist). Therefore, if a page or user control directly uses another user controls they should be compiled as well.

    During this step, all usages of user control types are fixed. The new names of the types are determined independent of the source - collection that contains path to the files is obtained by accessing the property of the parser's instance (FileDependencies).

  • Creating Win32 resource file (.res)

    For most of the pages and user controls ASP.NET runtime generates Win32 resource file that contains parts of the HTML content of the page or user control. The runtime passes the file to the compiler and its content is embedded in the generated assembly. The content is then used at runtime during the render phase of the page/user control. We will use this file in the compilation step.

All of the steps above are implemented in the WebApp.Web.Compilation.Compilation.GenerateCode(HttpContext,string) method.

The WebApp.Web.Compilation.Compilation class provides BatchGenerateCode that allows you to generate code for all files of the specified type (.aspx, .ascx, .asax) in the specified directory and its sub-directories.

Addition of external resources

Before adding any external files, one must invoke WebApp.Web.Compilation.Compilation.CreateCompilerParameters. This method merges the assembly references of all System.CodeDom.CodeCompileUnit instances and all resource files in a single resource file. This is required if one wishes to compile all of the generated code in a single assembly. Otherwise, each System.CodeDom.CodeCompileUnit can be compiled in a standalone assembly that includes only its own Win32 resource file's data. WebApp.Web.Compilation.Compilation.CreateCompilerParameters also alters the way the resources are loaded in the System.CodeDom.CodeCompileUnit instances. This is required because the names of the resources have been altered in order to merge them in a common resource file.

After a common resource file has been built, one can add other files as resources to it by using the WebApp.Web.Compilation.Compilation.AddFileToResources and WebApp.Web.Compilation.Compilation.AddFilesToResources methods. This method allows you to specify the custom name and type of the resources being added. The defaults are defined in WebApp.Web.Compilation.Runtime.ResourceManager.

For all the resources that you want to appear as external files at runtime an index is required to be built - this is done by the WebApp.Web.Compilation.Compilation.AddIndexToResources method. This method adds an index that contains the relative application's root names of the files and the names and types of their Win32 resource entries.

One can also build its own resource file (the WebApp.Web.Compilation.Compilation.AddFileToResources and WebApp.Web.Compilation.Compilation.AddFilesToResources have the path to the resource file as an argument) and compile it into a separate assembly by the WebApp.Web.Compilation.Compilation.CompileResoucesToAssembly method. At runtime, resources can be accessed by utilizing the methods in the WebApp.Web.Compilation.Runtime.ResourceManager type.

Compilation of generated code and resources into assembly

Compilation is done by the WebApp.Web.Compilation.Compilation.CompileToAssembly method. If you set the outputPath parameter to a value different from null a source code will also be generated and saved in the specified directory. Otherwise, the System.CodeDom.CodeCompileUnit instances will be directly compiled (actually the source code files are created by the .NET Framework in a temporary directory).

You can also generate source code files without compiling them into an assembly by using the WebApp.Web.Compilation.Compilation.GenerateSourceCode method.

Compiled application at runtime

At runtime the following should be taken care of:

  • Handling requests to compiled pages.
  • Ability to load compiled user controls.
  • Handling requests to embedded resources.

All of the above are implemented in the WebApp.Web.Compilation.Runtime assembly and WebApp.Web.Compilation is not required to run the application.

Handling requests to compiled pages

This is done by the WebApp.Web.Compilation.Runtime.CompiledPageHandlerFactory type. It implements the System.Web.IHttpHandlerFactory whose GetHandler method returns System.Web.IHttpHandler instance, which in case of .aspx page is of type System.Web.UI.Page.

To enable the handler the following should be included in web.config:

<system.web>
  <httphandlers>
    <add verb="*" path="*.aspx" 
      type="WebApp.Web.Compilation.Runtime.CompiledPageHandlerFactory,
                                  WebApp.Web.Compilation.Runtime" />
  </httpHandlers>
</system.web>

This will cause ASP.NET runtime to obtain request handlers for all requests to .aspx files through the WebApp.Web.Compilation.Runtime.CompiledPageHandlerFactory.GetHandler method. The method itself invokes WebApp.Web.Compilation.Runtime.CompiledControlManager.LoadPage to obtain System.Web.UI.Page instance.

WebApp.Web.Compilation.Runtime.CompiledControlManager.LoadPage behaves in the following way:

If the WebApp.Web.Compilation.Runtime.CompiledControlManager is initialized it tries to create an instance of the compiled type of the page. If the page is not compiled (i.e. type that corresponds to it is not found) and serving of non-compiled pages is allowed (this can be prohibited by configuration) it obtains the page instance in the same way as the built in .aspx handler (System.Web.UI.PageHandlerFactory) does - by invoking the System.Web.UI.PageParser.GetCompiledPageInstance(String, String, HttpContext) method. That allows combining compiled and not compiled pages.

If WebApp.Web.Compilation.Runtime.CompiledControlManager is not initialized System.Web.UI.PageParser.GetCompiledPageInstance(String, String, HttpContext) is used regardless of whether serving of non-compiled pages is allowed.

So WebApp.Web.Compilation.Runtime.CompiledControlManager.LoadPage will:

  • Try to find the type that corresponds to the requested page and create an instance of it.
  • If the corresponding type is not found and serving of non-compiled pages is allowed the page will be loaded in the same way as the standard .aspx handler does.

This means that if there are pages you have chosen not to compile, requests to them will be handled normally and that no one can substitute a page by putting an .aspx file in the directory as the method first checks for the compiled type. Moreover, you can forbid serving of non-compiled pages.

To configure WebApp.Web.Compilation.Runtime.CompiledControlManager the following needs to be included in web.config:

<configSections>
    <section name=
        "WebApp.Web.Compilation.Runtime.CompiledControlManager" 
        type="System.Configuration.NameValueSectionHandler,
        System, Version=1.0.5000.0, Culture=neutral, 
        PublicKeyToken=b77a5c561934e089"
        allowLocation="false" />
</configSections>
<WebApp.Web.Compilation.Runtime.CompiledControlManager>
    <add key="CompiledPageAssembly" value="UI" />
    <add key="CompiledUserControlAssembly" value="UI" />
    <add key="AllowNonCompiledPages" value="false" />
    <add key="AllowNonCompiledUserControls" value="false" />
</WebApp.Web.Compilation.Runtime.CompiledControlManager>

This configures both pages and user controls. You can specify different assemblies if applicable and you can skip the configuration of user controls for example if there are only pages compiled. The assemblies are loaded by using the System.Reflection.Assembly.Load method.

Alternatively, you can use these methods:

[Editor Note: Single space used to avoid scrolling.]

  • WebApp.Web.Compilation.Runtime.CompiledControlManager. InitCompiledControls
  • WebApp.Web.Compilation.Runtime.CompiledControlManager. InitCompiledPages
  • WebApp.Web.Compilation.Runtime.CompiledControlManager. InitCompiledUserControls

Ability to load compiled user controls

To load the compiled user control one needs to use the WebApp.Web.Compilation.Runtime.CompiledControlManager.LoadControl and WebApp.Web.Compilation.Runtime.CompiledControlManager.LoadTemplate methods instead of the System.Web.UI.TemplateControl.LoadControl(String) and System.Web.UI.TemplateControl.LoadTemplate(String) methods respectively. These methods have the same behavior as the WebApp.Web.Compilation.Runtime.CompiledControlManager.LoadPage method, i.e. they try to find the type that corresponds to the requested control and if this fails and serving of non-compiled user controls is allowed - fall back to the System.Web.UI.TemplateControl.LoadControl(String) and System.Web.UI.TemplateControl.LoadTemplate(String) methods.

Note that System.Web.UI.Page is a descendant of System.Web.UI.TemplateControl so all you need is to put the following methods in a class that derives from System.Web.UI.Page and will be the base class of your pages:

public new Control LoadControl( string virtualPath )
{
  return WebApp.Web.Compilation.Runtime.CompiledControlManager.LoadControl(
                                                     this, virtualPath );
}

public new ITemplate LoadTemplate( string virtualPath )
{
  return WebApp.Web.Compilation.Runtime.CompiledControlManager.LoadTemplate(
                                                          this, virtualPath );
}

Handling requests to embedded resources

To enable handling of requests to embedded resources as if they were external files you will need to do the following:

  • Map the required file name extensions to "aspnet_isapi.dll" at IIS Management Console for the application so that requests to such files are forwarded to ASP.NET runtime. Note that you should uncheck the checkbox that says, "Check that file exists" so the IIS will not verify if the file exists.
  • Configure ASP.NET runtime to use WebApp.Web.Compilation.Runtime.ResourceFileHandler as System.Web.IHttpHandler for requests to such files by including the following in web.config:
    <system.web>
        <httpHandlers>
            <add verb="*" path="*.html" 
                type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
            <add verb="*" path="*.css" 
                type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
            <add verb="*" path="*.js" 
                type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
            <add verb="*" path="*.gif" 
                type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
            <add verb="*" path="*.ico" 
                type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
            <add verb="*" path="*.png" 
                type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
            <add verb="*" path="*.bmp" 
                type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
            <add verb="*" path="*.jpg" 
                type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
            <add verb="*" path="*.jpeg" 
                type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
        </httpHandlers>
    </system.web>

    Add any other file name extensions required.

  • Configuring WebApp.Web.Compilation.Runtime.ResourceManager by including the following in web.config:
    <configSections>
        <section name="WebApp.Web.Compilation.Runtime.ResourceManager" 
            type="System.Configuration.NameValueSectionHandler, 
            System, Version=1.0.5000.0, Culture=neutral, 
            PublicKeyToken=b77a5c561934e089"
            allowLocation="false" />
        <section name=
            "WebApp.Web.Compilation.Runtime.ResourceManager.ContentTypes" 
            type="System.Configuration.NameValueSectionHandler, 
            System, Version=1.0.5000.0, Culture=neutral, 
            PublicKeyToken=b77a5c561934e089"
            allowLocation="false" />
    </configSections>
    <WebApp.Web.Compilation.Runtime.ResourceManager>
        <add key="ResourceAssembly" value="UI" />
        <add key="CacheLoadedResources" value="false" />
        <add key="AllowExternalResources" value="false" />
        <add key="PreferExternalResources" value="false" />
        <add key="DefaultContentType" value="application/octet-stream" />
    </WebApp.Web.Compilation.Runtime.ResourceManager>
    <WebApp.Web.Compilation.Runtime.ResourceManager.ContentTypes>
        <add key="html" value="text/html" />
        <add key="css" value="text/css" />
        <add key="js" value="application/x-javascript" />
        <add key="gif" value="image/gif" />
        <add key="ico" value="image/x-icon" />
        <add key="png" value="image/png" />
        <add key="bmp" value="image/bmp" />
        <add key="jpg" value="image/jpeg" />
        <add key="jpeg" value="image/jpeg" />
    </WebApp.Web.Compilation.Runtime.ResourceManager.ContentTypes>

    This specifies the assembly that contains the resources, some preferences and the content types that should be returned in HTTP response for each extension.

    You can do the same by using the WebApp.Web.Compilation.Runtime.ResourceManager.Init method and the WebApp.Web.Compilation.Runtime.ResourceManager.ContentTypes dictionary.

Briefly, all you need to do to ready the application is to include the following in the application's web.config:

<configuration>
    <configSections>
        <section name=
            "WebApp.Web.Compilation.Runtime.CompiledControlManager" 
            type="System.Configuration.NameValueSectionHandler, 
            System, Version=1.0.5000.0, Culture=neutral, 
            PublicKeyToken=b77a5c561934e089"
            allowLocation="false" />
        <section name=
            "WebApp.Web.Compilation.Runtime.ResourceManager" 
            type="System.Configuration.NameValueSectionHandler, 
            System, Version=1.0.5000.0, Culture=neutral, 
            PublicKeyToken=b77a5c561934e089"
            allowLocation="false" />
        <section name=
            "WebApp.Web.Compilation.Runtime.ResourceManager.ContentTypes" 
            type="System.Configuration.NameValueSectionHandler, 
            System, Version=1.0.5000.0, Culture=neutral, 
            PublicKeyToken=b77a5c561934e089"
            allowLocation="false" />
    </configSections>
    <WebApp.Web.Compilation.Runtime.CompiledControlManager>
        <add key="CompiledPageAssembly" value="UI" />
        <add key="CompiledUserControlAssembly" value="UI" />
        <add key="AllowNonCompiledPages" value="false" />
        <add key="AllowNonCompiledUserControls" value="false" />
    </WebApp.Web.Compilation.Runtime.CompiledControlManager>
    <WebApp.Web.Compilation.Runtime.ResourceManager>
        <add key="ResourceAssembly" value="UI" />
        <add key="CacheLoadedResources" value="false" />
        <add key="AllowExternalResources" value="false" />
        <add key="PreferExternalResources" value="false" />
        <add key="DefaultContentType" 
                           value="application/octet-stream" />
    </WebApp.Web.Compilation.Runtime.ResourceManager>
    <WebApp.Web.Compilation.Runtime.ResourceManager.ContentTypes>
        <add key="html" value="text/html" />
        <add key="css" value="text/css" />
        <add key="js" value="application/x-javascript" />
        <add key="gif" value="image/gif" />
        <add key="ico" value="image/x-icon" />
        <add key="png" value="image/png" />
        <add key="bmp" value="image/bmp" />
        <add key="jpg" value="image/jpeg" />
        <add key="jpeg" value="image/jpeg" />
    </WebApp.Web.Compilation.Runtime.ResourceManager.ContentTypes>
    <system.web>
      <httpHandlers>
        <add verb="*" path="*.aspx" 
          type="WebApp.Web.Compilation.Runtime.CompiledPageHandlerFactory,
                                         WebApp.Web.Compilation.Runtime" />
        <add verb="*" path="*.html" 
          type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
        <add verb="*" path="*.css" 
          type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
        <add verb="*" path="*.js" 
          type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
        <add verb="*" path="*.gif" 
          type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
        <add verb="*" path="*.ico" 
          type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
        <add verb="*" path="*.png" 
          type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
        <add verb="*" path="*.bmp" 
          type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
        <add verb="*" path="*.jpg" 
          type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
        <add verb="*" path="*.jpeg" 
          type="WebApp.Web.Compilation.Runtime.ResourceFileHandler,
                                         WebApp.Web.Compilation.Runtime" />
      </httpHandlers>
    </system.web>
</configuration>

to adjust these settings and to set extensions at IIS Management Console.

What will not work in a compiled application?

Caching for compiled user controls will not work if they are loaded by the using the WebApp.Web.Compilation.Runtime.CompiledControlManager.LoadControl method. That is if you apply @OutputCache directive or the System.Web.UI.PartialCachingAttribute to a compiled user control and it is not directly used in your pages or user controls, but loaded by the LoadControl method, the control's caching will not work. Caching for pages and user controls that are directly used should work but this is not tested.

Edit 2005-08-12: Caching works now. Source code and demo project are updated.

The demo

The demo is a small ASP.NET C# application that demonstrates how compilation and resource embedding works. Brief instructions are included on how to compile and run the application as well as the .zip files with compiled version and generated source code.

Licenses

WebApp.Web.Compilation is licensed under the GNU Lesser General Public License (although it is not required to run an application) and WebApp.Web.Compilation.Runtime is under the MIT License (which allows you to do anything with the code).

Final notes

The solution is tested with a relatively big ASP.NET application and works fine. However, it is possible that not all patterns of user control's usages are handled during the source code generation so the names of some of the user control types remain unfixed in the page or user control. That may prevent the source code from compiling and if not - that is because of automatically generated assemblies that contain the compiled code. You can make sure that there are no such references by examining the assembly or testing if the application works after it is deployed.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here