RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Compilation and Deployment in ASP.NET 2.0 : Page 4

Compilation and deployment are key features that ASP.NET developers should understand quite well. Find out how the process works and what options are available to compile and deploy your applications effectively.

Page Parsing
The first step in ASP.NET compilation really comes down to page parsing, where the ASP.NET compiler takes your ASPX page (or user control or master page) and parses it into code that you can compile and then execute. At a very high level, ASP.NET turns the ASPX page with its HTML markup, control definitions, and script content into a class that executes at run time. This process varies depending on the mechanism used (inline or CodeBeside) to set up your Web pages.

The simplest model of compilation for ASP.NET has always been the inline compilation mode. The idea of this model is that everything—code and markup—are contained in the single ASPX/ASCX/MASTER page with no external code anywhere. In the CodeBeside model you can store your user code in an external partial class, which allows cleaner separation of the presentation and application logic. I'll come back to CodeBeside a little later as it is a specialization of the general ASP.NET compilation model.

The inline model takes the content of an ASPX markup page and creates a single class out of this page at compile time. Inline pages don't use a special inheritance mechanism. Instead, ASP.NET creates a single page class derived from System.Web.UI.Page that contains both the page parse tree and your user code.

The page parsing mechanism used for inline pages also applies to CodeBeside pages with the main difference between the two models being that user code is applied. In the CodeBehind and CodeBeside models, ASP.NET inherits the generated class from a separate base class you create with your application-specific code. Inline pages, on the other hand, inherit directly from System.Web.UI.Page and have all code generated directly into this single class.

Look at the very simple inline ASPX page shown in Listing 1, which consists of a page with a couple of controls, a single event handler for a button click, and a custom property.

Figure 2 shows the layout of the generated class in .NET Reflector, which is a decompiler that lets you see the class structure and source code for a class and its implementation.

Figure 2: The class layout for an inline ASPX page generated by ASP.NET shows properties for each of the controls, your custom event methods and custom properties, and generated methods for building the parse tree. Note that an inline page inherits directly from System.Page.
When ASP.NET parses this inline ASPX page, it creates a class that consists of the control declarations as fields. It also adds any methods that you declare (such as the btnSayIt_Click event handler) to handle control or page-level events as well as any custom properties or methods you define in your code. In addition, the class generates code to create the page parse tree, which consists of a bunch of __BuildXXX methods that are responsible for constructing the control definitions and adding them to each naming container's Controls collection.

You can check out the generated class if you run your Web application in debug mode (include the tag <compilation debug="true" /> in the web.config file) by looking in your Temporary ASP.NET Files folder in the .NET Framework directory. On my machine, the path looks something like this.

     Temporary ASP.NET Files\compilationanddeployment\
The directory names below the virtual name will vary for your machine and there may be multiple directories—you have to find the right one by looking at timestamps or simply by looking at file content. In this directory you will find the compiled DLLs for the APP_CODE assembly, as well as any directory-level page and control assemblies. You can inspect these with Reflector as shown in Figure 2. This directory also holds a set of .cs or .vb files containing the generated ASP.NET classes used to compile the assemblies. ASP.NET generates the names for these assemblies and source files randomly based on a hashcode, so you have to open them individually to find the one you're interested in.

If you look at the .cs file for the generated class you will find a class that inherits from System.Web.UI.Page. The class contains a bunch of __BuildXXX methods that build the page parse tree. Listing 2 shows an excerpt of these methods that demonstrate how the page control tree is constructed.

At the highest level is the FrameworkInitialize method, which is called when the page class instantiates. This method handles "housekeeping" functionality for the page, such as managing file dependencies that determine which related pages/control references are pulled in for compilation and assembly referencing. It also validates the safety of request input (unless ValidateRequest="false"). But most important, it fires off the control tree creation by calling the __BuildControlTree method, which corresponds to the top-level node of the parse tree, which is the Page object.

The Page object is the top-level naming container of an ASP.NET page and it, in turn, contains other controls. __BuildControlTree sets up any custom properties of the Page object and then proceeds to add the top-level controls. The Page object typically consists of several literal sections that are static HTML text, an HtmlHeader control, and a Form control. The static text gets turned into Literal controls, which are added to the control tree. The individual __BuildXXX methods for each server control return fully-configured child control instances, which are then added to the container's Controls collection via the AddParsedSubObject method. There is one method for each control on the Web page with each container control referencing and instantiating its contained controls. The same logic applies to each of the containers. Each container contains literal content and controls, which are also parsed and added to the control tree. __BuildControlForm1 is an example of what a generated container method looks like. This method references the child control's __BuildXXX methods for each of the controls defined in the form, so the TextBox, Button, and Label controls are added by referencing their respective __BuildXXX methods.

You can also define class-level code inside <script runat="server"> tags in the markup. Any code within a <script> tag is placed at the top of the class and essentially adds to the class prototype. You can use this mechanism to add fields, properties, events, and methods—anything that you would normally do to add members to a class. You can also define your event handling methods in this <script> block as shown in Listing 1. Using the <script runat="server"> tag is most common in inline pages, but it also works in CodeBeside and CodeBehind pages.

Script Tags Complicate Matters
Like <script runat="server">, the <% %> tags allow you to inject code into the generated page class that ASP.NET creates as part of the compilation process. Think of the <script> tag as a class-level insertion point, while the <% %> tags are Render method insertion points that are executed at page rendering time. Code within <% %> tags is also complicated by the fact that it can intermix with static and markup code of the page. Take this example:

   <asp:Panel runat="Server" id="panelScript">
       for (int x = 0; x < 10;x++ )
   <asp:Label runat="server" ID="lblMessage" 
              Text="Counting: " />
   <%= x.ToString() %><br />
   <%} %>
ASP.NET does not allow you to add any controls to a container dynamically if <% %> tags are present.
In the preceding code, a script expression spans a literal control, a server control, and an embedded expression and could even span a whole bunch of controls wrapped around a structured program statement. And it's perfectly legal in ASP.NET.

To make code like this work, ASP.NET needs to override the rendering of the particular container in which any script code is hosted. It does this by using SetRenderMethodDelegate on the container and creating a custom rendering method that handles the code scenario as shown in Listing 3.

Rather than building up the control tree literal controls, ASP.NET adds server controls to the control tree only when <% %> tags are present for a container. To handle the literal content and the script markup, ASP.NET generates a custom rendering method. This method then explicitly writes out any static HTML content and any script expressions using an HTMLTextWriter. Any script code (<% %>) is generated as raw code of the method itself.

Because of this hard-coded mechanism, ASP.NET does not allow you to add controls to the container if any <% %> tags are defined in the container. Now you should understand the reason for receiving this error:

   The Controls collection cannot be modified because the control 
   contains code blocks (i.e., <% ... %>).
Because the method that renders the container with the script tags is hard coded and uses hard-coded indexes to any referenced controls, adding new controls would not work correctly. The indexes of any added controls would throw off the hard-coded index used by the generated method.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date