Inside the ASP.NET 2.0 Code Compilation Model

By John Lam of Wintellect

Version 2.0 of the .NET Frameworks introduces a number of important new features to the Common Language Runtime (CLR) and ASP.NET. This article discusses how these features are used in the new code compilation model of ASP.NET 2.0.

A Review of ASP.NET 1.x

Let's begin by reviewing the way that code was compiled under ASP.NET 1.x. The ASP.NET runtime dynamically compiles an ASP.NET page the first time a user requests it. First, it checks its cache to see whether that page has been compiled already.1 If it hasn't, it loads the .aspx file and generates a temporary source code file that implements a class that represents that ASP.NET page. It then invokes the appropriate command-line compiler2 to compile the generated source code into an assembly. Finally, the class that represents the ASP.NET page is instantiated to handle the incoming request.3

Let's use an example to discuss this lifecycle. Listings 1 and 2 show the code for default.aspx and its corresponding code-behind file, default.aspx.cs. These files were created using Visual Studio .NET 2003, and that the code-behind file was compiled into an assembly by the VS.NET 2003 build system.

There are several attributes to point out in the @Page declaration in Listing 1. There is a debug="true" attribute that forces the ASP.NET runtime to leave behind the intermediate source code file that it generates for the page. Also, there is an inherits="Test._Default" attribute that tells the ASP.NET runtime to derive the class it generates from our code-behind class.

Let's assume that these files are stored in your \Inetpub\wwwroot directory; you can force the compilation of this page by surfing to http://localhost/default.aspx. The ASP.NET runtime stores the temporary files that it creates during the page compilation process in [.NET System Directory]\Temporary ASP.NET Files\root\[x]\[y]\.4 If you examine this directory, you'll see a number of different files, at least one of which is a file with a .cs extension whose timestamp is the same as when you requested default.aspx. If you examine this file, you'll find that it should resemble Listing 3.

As you can see, the ASP.NET runtime generated code for a class called Default_aspx, the same as the filename of the .aspx file with the "." converted to an underscore. You'll see that the generated class derives from the class in the code-behind file, temp._Default, as instructed by the inherits attribute in default.aspx.

There are several (confusing) things to point out in this code. These things are confusing because of the division of responsibility that ASP.NET imposes on developers who choose to use code-behind.5 First, the Label1 and Button1 fields are declared in default.aspx.cs, which serves as the base class for the code that ASP.NET generates. These controls are actually created by the ASP.NET-generated code in the derived class; you'll find the code that creates these controls in two functions: __BuildControlLabel1 and __BuildControlButton1 in Listing 3. Finally, you'll see that the event handler for Button1's Click event is wired up programmatically in InitializeComponent in the base class, default.aspx.cs.

This division of responsibility between the base and derived classes is often a source of confusion for developers starting off in ASP.NET. While VS.NET handles much of the details related to code-behind programming, we'll see that ASP.NET 2.0 provides a much simpler solution that doesn't require tool support.

ASP.NET 2.0

Now, let's now turn our attention to how things have changed in ASP.NET 2.0. First, the division of responsibility between base and derived classes goes away. Second, you no longer have to separately compile your user-defined code into an assembly that gets copied into the application's \bin directory. All code, both generated and user-defined is now compiled by the ASP.NET runtime into a single class.

Listing 4 shows the code for default.aspx. There are three things to notice in this file. First, there is a new compileWith attribute in the @Page declaration. This tells the ASP.NET runtime the name of the code separation file that contains user-defined code. Second, the ASP.NET 1.x inherits attribute is no longer present. Recall that this attribute told the ASP.NET 1.x runtime what class to derive the ASP.NET page from. Third, the event handler for Button1's Click event is hooked up using an attribute in default.aspx. This is quite unlike the way events are hooked up in ASP.NET 1.x in the previous example.

Listing 5 shows the new code for the code separation file, default.aspx.cs. Notice that this file only contains the event handler for Button1's Click event. The reason this code is so much simpler is due to a language feature called partial classes that is new to both C# and VB.NET. This feature lets a class definition span multiple source files, which is how VS.NET and ASP.NET can hide all of the standard initialization code from you. To understand how partial classes work, we need to take a closer look at the code that is generated by the ASP.NET runtime.

First, you need to run the application and force the ASP.NET runtime to dynamically compile your page. When you run your application using VS.NET Whidbey, you'll notice two new things. First, there isn't a build step; you immediately start running your Web application. Second, you'll notice that your ASP.NET code no longer runs in IIS during development. Instead, it runs in a new managed Web server called Visual Web Developer Web Server. This new design means that you don't need to install or configure IIS on a developer's computer. It also means that you can develop ASP.NET code using a restricted user account without having to reconfigure ASP.NET first.6

After we run our application for the first time, we need to find where ASP.NET places the generated files. As it turns out, they're in a different location now. Because the Visual Web Developer Web Server runs using your credentials, you'll find the files in Local Settings\Temp\Temporary ASP.NET Files\[virtual root]\[x]\[y]. When you look in this directory, you should find two .cs files that resemble the code in Listings 6 and 7.

The code in Listing 6 is code that the ASP.NET runtime generated for the default.aspx page. Notice that it declares part of the code for a class called Default_aspx; its class definition is declared using the C# partial keyword. This code is combined at compile-time with the code in Listing 7, which contains the rest of the definition of the Default_aspx class. You'll notice right away the strong resemblance between the code in this listing and the code in Listing 5. Both of these files were compiled using the C# compiler into a new assembly.7

Because ASP.NET now compiles all of the code for your ASP.NET pages for you, what happens to code that isn't part of an ASP.NET page? There is a new feature in ASP.NET 2.0 that lets you put all related code in a subdirectory called Code.8 This can be other source code files, .wsdl files, or .xsd files.9 ASP.NET will invoke the appropriate command-line tools to compile these source code files at run-time.

Conclusion

The new page compilation model in ASP.NET 2.0 is a significant improvement over ASP.NET 1.x. It's simpler because all of the code for an ASP.NET page now resides in a single class; there isn't a forced division of labor between the base class and the derived class. It also reduces developer error; in ASP.NET 1.x, you could forget to recompile10 or copy your code-behind assemblies to the \bin directory of your Web application. You could also accidentally "change" the code in your code-behind file that hooks up your event handlers.

Now, all of your user-defined and ASP.NET-generated code resides inside a single class that is generated by the ASP.NET runtime. This means that you don't need special developer tool support to make code-behind programming approachable. Because ASP.NET now takes care of compiling all of the code in your Web application, it means that you have far fewer opportunities to make a mistake while building or deploying your application.

Finally, there is one other compilation feature in ASP.NET 2.0 that is great for build/deployment scenarios. You can now pre-compile your entire application into a single set of assemblies. This is done using a new command-line utility called aspnet_compiler. This makes it possible to deploy an ASP.NET application without copying any source code files to a production server. It is also great for automated build scenarios since aspnet_compiler can verify that all code in an ASP.NET application actually compiles for a given build.

About the Author

John Lam is the co-author of Wintellect's popular Programming ASP.NET class. He is a co-author of Essential XML with Don Box and Aaron Skonnard. He maintains a popular weblog at http://www.iunknown.com, and writes a popular online newsletter: Practical Eye for the .NET Guy.

<%@ Page debug="true" language="c#" Codebehind="Default.aspx.cs"
    AutoEventWireup="false" Inherits="Test._Default" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
  <HEAD>
    <title>Default</title>
    <meta name="GENERATOR" Content="Microsoft Visual Studio
                                    .NET 7.1">
    <meta name="CODE_LANGUAGE" Content="C#">
    <meta name="vs_defaultClientScript" content="JavaScript">
    <meta name="vs_targetSchema"
          content="http://schemas.microsoft.com/intellisense/ie5">
  </HEAD>
  <body>
    <form id="Form1" method="post" runat="server">
      <P>
        <asp:Label id="Label1" runat="server">Hello, World!
        </asp:Label></P>
      <P>
        <asp:Button id="Button1" runat="server" Text="Click Me">
        </asp:Button></P>
    </form>
  </body>
</HTML>

Listing 1: The default.aspx file in ASP.NET 1.x.

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace Test
{
  /// <summary>
  /// Summary description for _Default.
  /// </summary>
  public class _Default : System.Web.UI.Page
  {
    protected System.Web.UI.WebControls.Label Label1;
    protected System.Web.UI.WebControls.Button Button1;

    private void Page_Load(object sender, System.EventArgs e)
    {
      // Put user code to initialize the page here
    }

    #region Web Form Designer generated code
    override protected void OnInit(EventArgs e)
    {
      //
      // CODEGEN: This call is required by the ASP.NET Web Form
      // Designer.
      //
      InitializeComponent();
      base.OnInit(e);
    }

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      this.Button1.Click +=
           new System.EventHandler(this.Button1_Click);
      this.Load += new System.EventHandler(this.Page_Load);

    }
    #endregion

    private void Button1_Click(object sender, System.EventArgs e)
    {
      Label1.Text = "Clicked me!";
    }
  }
}

Listing 2: The code-behind file: default.aspx.cs in ASP.NET 1.x.

//-----------------------------------------------------------------
// <autogenerated>
//     This code was generated by a tool.
//     Runtime Version: 1.1.4322.573
//
//     Changes to this file may cause incorrect behavior and will
//     be lost if the code is regenerated.
// </autogenerated>
//-----------------------------------------------------------------

namespace ASP {
    using System;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Configuration;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Web;
    using System.Web.Caching;
    using System.Web.SessionState;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.HtmlControls;
    using ASP;


    [System.Runtime.CompilerServices.CompilerGlobalScopeAttribute()]
    public class Default_aspx : Test._Default,
           System.Web.SessionState.IRequiresSessionState {


        #line 12 "c:\inetpub\wwwroot\Test\Default.aspx"
        protected System.Web.UI.HtmlControls.HtmlForm Form1;

        #line default
        #line hidden

        private static bool __initialized = false;

        private static object __stringResource;

        private static
                System.Collections.ArrayList __fileDependencies;

        public Default_aspx() {
            System.Collections.ArrayList dependencies;
            if ((ASP.Default_aspx.__initialized == false)) {
                ASP.Default_aspx.__stringResource =
                    System.Web.UI.TemplateControl.
                    ReadStringResource(typeof(ASP.Default_aspx));
                dependencies = new System.Collections.ArrayList();
                dependencies.Add("c:\\inetpub\\wwwroot\\Test\\
                                  Default.aspx");
                ASP.Default_aspx.__fileDependencies = dependencies;
                ASP.Default_aspx.__initialized = true;
            }
            this.Server.ScriptTimeout = 30000000;
        }

        protected override bool SupportAutoEvents {
            get {
                return false;
            }
        }

        protected ASP.Global_asax ApplicationInstance {
            get {
                return ((ASP.Global_asax)(this.Context.
                                          ApplicationInstance));
            }
        }
        
        public override string TemplateSourceDirectory {
            get {
                return "/Test";
            }
        }

        private System.Web.UI.Control __BuildControlLabel1() {
            System.Web.UI.WebControls.Label __ctrl;

            #line 14 "c:\inetpub\wwwroot\Test\Default.aspx"
            __ctrl = new System.Web.UI.WebControls.Label();

            #line default
            #line hidden
            this.Label1 = __ctrl;

            #line 14 "c:\inetpub\wwwroot\Test\Default.aspx"
            __ctrl.ID = "Label1";

            #line default
            #line hidden
            System.Web.UI.IParserAccessor __parser =
                   ((System.Web.UI.IParserAccessor)(__ctrl));

            #line 14 "c:\inetpub\wwwroot\Test\Default.aspx"
            __parser.AddParsedSubObject(new System.Web.UI.
                     LiteralControl("Hello, World!"));

            #line default
            #line hidden
            return __ctrl;
        }

        private System.Web.UI.Control __BuildControlButton1() {
            System.Web.UI.WebControls.Button __ctrl;

            #line 16 "c:\inetpub\wwwroot\Test\Default.aspx"
            __ctrl = new System.Web.UI.WebControls.Button();

            #line default
            #line hidden
            this.Button1 = __ctrl;

            #line 16 "c:\inetpub\wwwroot\Test\Default.aspx"
            __ctrl.ID = "Button1";

            #line default
            #line hidden

            #line 16 "c:\inetpub\wwwroot\Test\Default.aspx"
            __ctrl.Text = "Click Me";

            #line default
            #line hidden
            return __ctrl;
        }

        private System.Web.UI.Control __BuildControlForm1() {
            System.Web.UI.HtmlControls.HtmlForm __ctrl;

            #line 12 "c:\inetpub\wwwroot\Test\Default.aspx"
            __ctrl = new System.Web.UI.HtmlControls.HtmlForm();

            #line default
            #line hidden
            this.Form1 = __ctrl;

            #line 12 "c:\inetpub\wwwroot\Test\Default.aspx"
            __ctrl.ID = "Form1";

            #line default
            #line hidden

            #line 12 "c:\inetpub\wwwroot\Test\Default.aspx"
            __ctrl.Method = "post";

            #line default
            #line hidden
            System.Web.UI.IParserAccessor __parser =
                   ((System.Web.UI.IParserAccessor)(__ctrl));

            #line 12 "c:\inetpub\wwwroot\Test\Default.aspx"
            __parser.AddParsedSubObject(new System.Web.UI.
                     LiteralControl("\r\n\t\t\t<P>\r\n\t\t\t\t"));

            #line default
            #line hidden

            #line 12 "c:\inetpub\wwwroot\Test\Default.aspx"
            this.__BuildControlLabel1();

            #line default
            #line hidden

            #line 12 "c:\inetpub\wwwroot\Test\Default.aspx"
            __parser.AddParsedSubObject(this.Label1);

            #line default
            #line hidden

            #line 12 "c:\inetpub\wwwroot\Test\Default.aspx"
            __parser.AddParsedSubObject(new System.Web.UI.
                     LiteralControl("</P>\r\n\t\t\t
                                     <P>\r\n\t\t\t\t"));

            #line default
            #line hidden

            #line 12 "c:\inetpub\wwwroot\Test\Default.aspx"
            this.__BuildControlButton1();

            #line default
            #line hidden

            #line 12 "c:\inetpub\wwwroot\Test\Default.aspx"
            __parser.AddParsedSubObject(this.Button1);

            #line default
            #line hidden

            #line 12 "c:\inetpub\wwwroot\Test\Default.aspx"
            __parser.AddParsedSubObject(new System.Web.UI.
                     LiteralControl("</P>\r\n\t\t"));

            #line default
            #line hidden
            return __ctrl;
        }

        private void __BuildControlTree(System.Web.UI.
                                        Control __ctrl) {
            System.Web.UI.IParserAccessor __parser =
                   ((System.Web.UI.IParserAccessor)(__ctrl));

            #line 1 "c:\inetpub\wwwroot\Test\Default.aspx"
            __parser.AddParsedSubObject(this.
                     CreateResourceBasedLiteralControl(0, 395,
                                                       true));

            #line default
            #line hidden

            #line 1 "c:\inetpub\wwwroot\Test\Default.aspx"
            this.__BuildControlForm1();

            #line default
            #line hidden

            #line 1 "c:\inetpub\wwwroot\Test\Default.aspx"
            __parser.AddParsedSubObject(this.Form1);

            #line default
            #line hidden

            #line 1 "c:\inetpub\wwwroot\Test\Default.aspx"
            __parser.AddParsedSubObject(new System.Web.UI.
                     LiteralControl("\r\n\t</body>\r\n</HTML>
                                     \r\n"));

            #line default
            #line hidden
        }

        protected override void FrameworkInitialize() {
            SetStringResourcePointer(ASP.Default_aspx.
                                     __stringResource, 395);
            this.__BuildControlTree(this);
            this.FileDependencies = ASP.Default_aspx.
                                    __fileDependencies;
            this.EnableViewStateMac = true;
            this.Request.ValidateInput();
        }

        public override int GetTypeHashCode() {
            return 666475897;
        }
    }
}

Listing 3: The source code file that ASP.NET 1.x generates for default.aspx.

<%@ page debug="true" language="C#" compilewith="Default.aspx.cs"
         classname="ASP.Default_aspx"%>
<html>
  <head runat="server">
    <title>Untitled Page</title>
  </head>
  <body>
    <form runat="server">
      <asp:label id="Label1" runat="server">Hello, World!
      </asp:label>
      <br />
      <br />
      <asp:button id="Button1" runat="server" text="Click Me"
                  onclick="Button1_Click" />
    </form>
  </body>
</html>

Listing 4: The default.aspx file in ASP.NET 2.0.

using System;

namespace ASP
{
  public partial class Default_aspx
  {
    void Button1_Click(object sender, System.EventArgs e)
    {
      // And Bob's your uncle.
      Label1.Text = "Clicked me!";
    }
  }
}

Listing 5: The code separation file: default.aspx.cs in ASP.NET 2.0.

//-----------------------------------------------------------------
// <autogenerated>
//     This code was generated by a tool.
//     Runtime Version:1.2.30703.4
//
//     Changes to this file may cause incorrect behavior and will
//     be lost if the code is regenerated.
// </autogenerated>
//-----------------------------------------------------------------

namespace ASP {

    #line 433 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Web.Security;

    #line default
    #line hidden

    #line 432 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Web.SessionState;

    #line default
    #line hidden

    #line 430 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Web;

    #line default
    #line hidden

    #line 9 "c:\dev\Test\Default.aspx"
    using System.Web.UI.WebControls;

    #line default
    #line hidden

    #line 428 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Text;

    #line default
    #line hidden

    #line 424 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Collections;

    #line default
    #line hidden

    #line 425 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Collections.Specialized;

    #line default
    #line hidden

    #line 426 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.ComponentModel;

    #line default
    #line hidden

    #line 438 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Web.UI.HtmlControls;

    #line default
    #line hidden

    #line 427 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Configuration;

    #line default
    #line hidden

    #line 434 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Web.Personalization;

    #line default
    #line hidden

    #line 423 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System;

    #line default
    #line hidden

    #line 431 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Web.Caching;

    #line default
    #line hidden

    #line 435 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Web.UI;

    #line default
    #line hidden

    #line 429 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Text.RegularExpressions;

    #line default
    #line hidden

    #line 436 "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703\
                  Config\machine.config"
    using System.Web.UI.Imaging;

    #line default
    #line hidden


    [System.Runtime.CompilerServices.CompilerGlobalScopeAttribute()]
    public partial class Default_aspx : System.Web.UI.Page,
           System.Web.SessionState.IRequiresSessionState {


        #line 4 "c:\dev\Test\Default.aspx"
        private System.Web.UI.HtmlControls.HtmlHead @__control2;

        #line default
        #line hidden


        #line 9 "c:\dev\Test\Default.aspx"
        protected System.Web.UI.WebControls.Label Label1;

        #line default
        #line hidden


        #line 12 "c:\dev\Test\Default.aspx"
        protected System.Web.UI.WebControls.Button Button1;

        #line default
        #line hidden


        #line 8 "c:\dev\Test\Default.aspx"
        private System.Web.UI.HtmlControls.HtmlForm @__control3;

        #line default
        #line hidden

        private static bool @__initialized = false;

        private static object @__fileDependencies;

        public Default_aspx() {
            string[] dependencies;
            if ((ASP.Default_aspx.@__initialized == false)) {
                dependencies = new string[2];
                dependencies[0] = "~/Default.aspx";
                dependencies[1] = "~/Default.aspx.cs";
                ASP.Default_aspx.@__fileDependencies =
                    this.GetWrappedFileDependencies(dependencies);
                ASP.Default_aspx.@__initialized = true;
            }
            this.Server.ScriptTimeout = 30000000;
        }

        protected ASP.HttpPersonalization Profile {
            get {
                return ((ASP.HttpPersonalization)
                        (this.Context.Profile));
            }
        }

        protected System.Web.HttpApplication ApplicationInstance {
            get {
                return ((System.Web.HttpApplication)
                        (this.Context.ApplicationInstance));
            }
        }

        public override string AppRelativeTemplateSourceDirectory {
            get {
                return "~";
            }
        }

        public override System.Web.UI.TemplateControl
               TemplateControl {
            get {
                return this;
            }
        }

        private System.Web.UI.Control @__BuildControl__control2() {
            System.Web.UI.HtmlControls.HtmlHead @__ctrl;

            #line 4 "c:\dev\Test\Default.aspx"
            @__ctrl = new System.Web.UI.HtmlControls.HtmlHead();
            
            #line default
            #line hidden
            this.@__control2 = @__ctrl;
            System.Web.UI.IParserAccessor @__parser =
                   ((System.Web.UI.IParserAccessor)(@__ctrl));
            
            #line 4 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(new System.Web.UI.
                      LiteralControl("\r\n
                          <title>Untitled Page</title>\r\n"));

            #line default
            #line hidden
            return @__ctrl;
        }

        private System.Web.UI.Control @__BuildControlLabel1() {
            System.Web.UI.WebControls.Label @__ctrl;

            #line 9 "c:\dev\Test\Default.aspx"
            @__ctrl = new System.Web.UI.WebControls.Label();

            #line default
            #line hidden
            this.Label1 = @__ctrl;

            #line 9 "c:\dev\Test\Default.aspx"
            @__ctrl.ID = "Label1";

            #line default
            #line hidden
            System.Web.UI.IParserAccessor @__parser =
                   ((System.Web.UI.IParserAccessor)(@__ctrl));

            #line 9 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(new System.Web.UI.
                      LiteralControl("Hello, World!"));

            #line default
            #line hidden
            return @__ctrl;
        }

        private System.Web.UI.Control @__BuildControlButton1() {
            System.Web.UI.WebControls.Button @__ctrl;

            #line 12 "c:\dev\Test\Default.aspx"
            @__ctrl = new System.Web.UI.WebControls.Button();

            #line default
            #line hidden
            this.Button1 = @__ctrl;

            #line 12 "c:\dev\Test\Default.aspx"
            @__ctrl.ID = "Button1";

            #line default
            #line hidden

            #line 12 "c:\dev\Test\Default.aspx"
            @__ctrl.Text = "Click Me";

            #line default
            #line hidden

            #line 12 "c:\dev\Test\Default.aspx"
            @__ctrl.Click +=
                    new System.EventHandler(this.Button1_Click);

            #line default
            #line hidden
            return @__ctrl;
        }

        private System.Web.UI.Control @__BuildControl__control3() {
            System.Web.UI.HtmlControls.HtmlForm @__ctrl;

            #line 8 "c:\dev\Test\Default.aspx"
            @__ctrl = new System.Web.UI.HtmlControls.HtmlForm();

            #line default
            #line hidden
            this.@__control3 = @__ctrl;
            System.Web.UI.IParserAccessor @__parser =
                   ((System.Web.UI.IParserAccessor)(@__ctrl));

            #line 8 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(new System.Web.UI.
                      LiteralControl("\r\n        "));

            #line default
            #line hidden

            #line 8 "c:\dev\Test\Default.aspx"
            this.@__BuildControlLabel1();

            #line default
            #line hidden

            #line 8 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(this.Label1);

            #line default
            #line hidden

            #line 8 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(new System.Web.UI.
                      LiteralControl("\r\n        <br />
                                      \r\n        <br />
                                      \r\n        "));

            #line default
            #line hidden

            #line 8 "c:\dev\Test\Default.aspx"
            this.@__BuildControlButton1();

            #line default
            #line hidden

            #line 8 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(this.Button1);

            #line default
            #line hidden

            #line 8 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(new System.Web.UI.
                      LiteralControl("\r\n\r\n    "));

            #line default
            #line hidden
            return @__ctrl;
        }

        private void @__BuildControlTree(Default_aspx @__ctrl) {
            System.Web.UI.IParserAccessor @__parser =
                   ((System.Web.UI.IParserAccessor)(@__ctrl));

            #line 1 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(new System.Web.UI.
                      LiteralControl("\r\n\r\n<html>\r\n"));

            #line default
            #line hidden

            #line 1 "c:\dev\Test\Default.aspx"
            this.@__BuildControl__control2();

            #line default
            #line hidden

            #line 1 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(this.@__control2);

            #line default
            #line hidden

            #line 1 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(new System.Web.UI.
                      LiteralControl("\r\n<body>\r\n    "));

            #line default
            #line hidden

            #line 1 "c:\dev\Test\Default.aspx"
            this.@__BuildControl__control3();

            #line default
            #line hidden

            #line 1 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(this.@__control3);

            #line default
            #line hidden

            #line 1 "c:\dev\Test\Default.aspx"
            @__parser.AddParsedSubObject(new System.Web.UI.
                      LiteralControl("\r\n</body>\r\n</html>\r\n"));

            #line default
            #line hidden
        }

        protected override void FrameworkInitialize() {
            base.FrameworkInitialize();
            this.@__BuildControlTree(this);
            this.AddWrappedFileDependencies(ASP.Default_aspx.
                                            @__fileDependencies);
            this.Request.ValidateInput();
        }

        public override int GetTypeHashCode() {
            return -1362848695;
        }
    }
}

Listing 6: The source code file that ASP.NET 2.0 generates for default.aspx.

#line 1 "c:\dev\Test\Default.aspx.cs"
using System;

namespace ASP
{
  public partial class Default_aspx
  {
    void Button1_Click(object sender, System.EventArgs e)
    {
      // And Bob's your uncle.
      Label1.Text = "Clicked me!";
    }
  }
}

#line default
#line hidden

Listing 7: The source code file that ASP.NET 2.0 generates for default.aspx.cs.

End Notes

1 ASP.NET also invalidates the cache if a developer changes the .aspx file.

2 ASP.NET 1.x supports C#, VB.NET, and JScript.NET out of the box.

3 For completeness, I have to point out that there is an additional compilation step that occurs when the code for the class is Just-In-Time compiled into native code.

4 [.NET System Directory] maps to c:\Windows\Microsoft.Net\Framework\V1.1.4322\ on my V1.1 .NET computer. [x] and [y] are two randomly generated directory names.

5 This is, of course, virtually everybody who uses VS.NET since this is how the VS.NET wizards set up a project. Developers who use the free Web Matrix IDE (http://www.asp.net/webmatrix/default.aspx), however, typically do not use code-behind.

6 In the past, you had to configure the ASP.NET worker process, aspnet_wp.exe, to run using your credentials. Visual Web Server runs using your credentials by default.

7 If you're interested in the command line switches that were used to compile your application, you'll find them in the .cmdline file in the temporary ASP.NET files directory.

8 You can configure additional directories in your web.config file using a <codeSubDirectories> element.

9 Custom file types can also be compiled. See the <buildProviders> section of machine.config for more details.

10 This typically occurs only in cases where developers aren't using VS.NET to do code-behind programming.

# # #