|
|
| White Papers Home | White Papers | Message Board | Search | Products | Purchase | News | WebLog | Tools |
Compilation and Deployment in ASP.NET 2.0
By Rick Strahl
Last Update: February 12, 2008
Compilation and deployment in ASP.NET 2.0 has brought some of the biggest changes to the ASP.NET development model. As developers we use page compilation all day long and deployment is something we all have to worry about sooner or later. A lot has changed in both areas and they can affect how you build and deploy your applications so understanding how the model works is crucial. In this article I’ll provide a detailed discussion of how compilation and deployment works. I’ll also talk about the new Web Deployment Projects and Web Application Projects add-ins that Microsoft is providing to ease some of the pain points of the stock functionality shipped with ASP.NET 2.0 and Visual Studio 2005.
What's covered:
Comments or Discussion
of this article: http://west-wind.com/WebLog/posts/3009.aspx ASP.NET 2.0's release has brought many welcome changes and additions to the ASP.NET model of Web Development. One of the key areas of change is the compilation and deployment aspect of ASP.NET 2.0 which changes quite drastically in the new version and represents some of the more controversial changes in the 2.0 release. In this article I’ll look at the stock project model and show how the different compilation models work. We’ll look at the project system, the page parsing mechanism and compilation of pages and how applications are deployed. Because there have been a number of concerns raised with stock projects, Microsoft recently released a couple of add-ins for Visual Studio that address some of the shortcomings and complaints from the developer community. The tools are Web Deployment Projects and Web Application Projects and I’ll look at these two tools and explain how they complement or replace stock projects. The New Project Model in ASP.NET 2.0The new project model in ASP.NET 2.0 is tightly linked with the new compilation and deployment features. Microsoft completely overhauled the way that page compilation is accomplished in the new version without breaking the way that original compilation worked in ASP.NET 1.1. Making things easierThe motivation behind these changes in the model was to make it easier to use ASP.NET and Visual Studio for Web development. In Visual Studio 2003, creating a new project - or even worse trying to open an existing project moved from another machine - was a fairly involved process that required creating a virtual directory, ensuring the FrontPage extensions were installed and making sure that the project file was configured correctly to point at the virtual directory before you could even start to look at the project. It’s much easier to perform these tasks in Visual Studio 2005. The changes in the new project model are geared to
make it quicker and easier to get a new project up and running or to
open an existing project. You can now open a project simply by pointing
at a directory and ASP.NET and Visual Studio both can figure out from
the directory structure how display, compile and run that project
without any manual configuration or an explicit compilation step. As
shown in Figure 1 to open an existing Web Project you can simply point
at a directory in the file system and open it as a Web site.
Figure 1 – Opening and creating of projects is much easier in Visual Studio 2005 by simply selecting a directory in the file system. Once opened the directory acts as the project, providing the file content for the project – there’s no explicit project file in Web Projects. The new project system allows you to open projects from a directory, from a local IIS, an FTP site and a Remote Site. Local IIS uses the IIS metabase to find the directory on the local machine. Other than that there not a whole lot of difference from a file based project. FTP Site opens a renite site through an FTP connection and it uses FTP to figure out the project structure in much the same way as a file based project does., so everything gets pulled into the project. You can also open a Remote Site, which like VS2003 requires the FrontPage extensions installed on the remote or local server (accessed through HTTP). The Remote Site configuration is more rigid in that you have to explicitly add files to the project as it doesn’t auto-detect content. This project opening format is useful if you want to remotely connect to another machine, but it’s also useful for local project that have lots of static content you don’t want to automatically be part of your project. For example, if you have a root Web site that has subdirectories that are in turn virtual directories a Remote Site prevents importing all the child virtual directories, which is not the case with file projects. The file system project is the easiest and most common way to open a project. Add to that the new built-in Web Server that ships with Visual Studio and you can be up and running a new or existing Web application instantly without having to configure anything. Open the directory as a File Web Project in Visual Studio, click View in Browser and your page runs. It’s very easy and this is surely what the ASP.NET designers were shooting for: Making ASP.NET less daunting when first setting up to create or run an existing application. Easy on the Surface – Complex underneathBut while the overall operation gets easier, the underlying model used to provide this simplicity is actually very complex and requires a lot of help from ASP.NET internals to make it happen. Compared with the ASP.NET 1.1 CodeBehind model which was purely based on simple inheritance, this new model uses runtime control and event generation, partial classes, inferred referencing of assemblies, delayed runtime compilation and single page assembly compilation along with a lot of help of the ASP.NET runtime and Visual Studio to make it all work both at runtime and during design time inside of Visual Studio 2005. There is a lot of magic that happens inside the ASP.NET runtime to allow features such as individual page compilation, ensuring proper linking of ‘reference’ assemblies and making sure that the development environment can display accurate Intellisense information on all of this inferred type information that logistically wouldn’t be available until runtime. What this means is that you don’t plainly see all there is to see at design time in terms of code and you’re relying on Visual Studio to do its magic to provide you a rich design time experience with Intellisense. Most of the time you don’t need to worry about these internals as they are encapsulated in the ASP.NET core engine, but once you step beyond the simple scenarios, you as the developer have to actually understand all of the intricacies of this complex model in order to make the model work for you. Depending on the type of applications this will affect some developers more than others. I think developers building and working reusable and extensible Web frameworks with lots of generic code in particular will hit the limitations of the new project model soonest. DeploymentCompilation in ASP.NET 2.0 is accomplished by running the new ASPNET_COMPILER.EXE against a Web application. The compiler has many options to compile your projects which include in-place compilation which requires source code distribution, pre-compiled compilation into all binary code, and partial compilation which compiles your user code, but lets you distribute and modify the ASPX markup pages. There are at least 20 different compilation combinations and none of them are likely to be exactly what you probably would hope for - most combinations produce non-repeatable installs and none of the stock combinations create a single deployable assembly that most developers would expect from a pre-compiled application. Deployment with stock projects only is complicated unless you do an in-place deployment. In-place deployment is simple to understand – you simply copy your entire development environment including source code to the server. All the other options require multiple steps of deleting of files and then re-copying files which disrupts application uptime on the server and requires a fairly strict deployment regimen to work reliably. To address some of the shortcomings with compilation, Microsoft released Web Deployment Projects (WDP) which provides a mechanism to post-process the output from the ASPNET_COMPILER.EXE and create single assembly. This add-in is now available from the Microsoft Web site. I’ll talk more about deployment later in the article. How things work in ASP.NET 1.xIf you’re like me, you probably come from an ASP.NET 1.x background and you’re familiar with that model. To put things in perspective let’s first review how things work in ASP.NET 1.x and VS2003. In ASP.NET 1.1 the model is based primarily on inheritance. When using the default CodeBehind model that Visual Studio 2003 promotes, you have a CodeBehind class that acts as the base class for the final ASPX page class that ASP.NET 1.x generates at runtime. So there are two classes – one that contains your user code and the control definitions as well as event hookups and a class that ASP.NET generates that contains the page parse tree and a code representation of all of the HTML markup and control syntax that lives in the ASPX page. The CodeBehind base class includes control definitions, event hookups and of course your page specific code which handles the various page level events like Page_Load and your event triggers like button clicks or change events. The control definitions and event hookup are generated by Visual Studio at design time, which has been a sore point in VS2003 since it does from time to time mangle the event hookups, mysteriously loosing events you had previously mapped to page handlers. When you run the application, ASP.NET dynamically creates a new class that contains the page control tree, which is responsible for turning the HTML markup, script tags and control definitions on the page into executable code. Basically each control is parsed into a method that assigns the control attributes to properties. Containers call child methods to set up controls so this is why it’s called a parse tree – it can be potentially many levels deep depending on the page control hierarchy. This generated class inherits from your CodeBehind class and your control definitions and event handling code is accessible to this class. The additional code generated then is responsible for rendering the page.
Compilation of all the CodeBehind code in ASP.NET 1.x is handled explicitly by Visual Studio (or the command line compilers) which creates a single assembly from all of your CodeBehind code of all pages, controls and classes defined in the project. Any markup pages (ASXP/ASCX/ASHX etc.) on the other hand are always parsed and compiled at runtime. ASP.NET dynamically creates this page class and compiles it into an individual assembly in the Temporary ASP.NET Files folder, one assembly per page. This assembly in turn imports a reference to the CodeBehind assembly, so all the CodeBehind page and control classes and support types are always accessible to the generated page class. Although each page and control compiles into a single individual assembly, each page or control has a dependency on the single CodeBehind assembly that contains the user code for all of the pages and controls in the project. It’s a relatively simple, yet elegant model and it has worked well for ASP.NET 1.x. In addition to the CodeBehind model ASP.NET 1.x also supports single page, inline markup pages. In this model the ASPX page (or ASCX control) contains both the HTML markup along with all the required script code placed inside of <% %> or <script runat="server"> tags. In this page model all compilation occurs at runtime and ASP.NET parses the single page into a the control tree class directly inherited from System.Web.UI.Page. The class contains embedded user code inside of script tags, which is parsed into the appropriate class areas. Server <script> tags become class members, so event handler methods, custom methods and property definitions are created inside of server <script> tags. You can also use Inline code snippets which use <% %> (for code blocks) or <%= %> (expressions). The <% %> are parsed inline to the rendering code. Like the CodeBehind model, this class is then compiled into a single assembly stored in the Temporary ASP.NET files folder. The single page model is very simple and there is no explicit compilation. Unfortunately, VS2003 did not support the single page model very well, and so it was rarely used, and maybe for good reason, as using the CodeBehind model encourages separating your Markup and code. This single page model has carried over to ASP.NET 2.0 and changed very little in the process, but VS2005 now fully supports creating single Inline pages.
Page Compilation in ASP.NET 2.0At the core of the changes in ASP.NET 2.0 is the new way that page compilation works in ASP.NET 2.0. The key difference is that ASP.NET itself takes over much more control when compiling your Web application. By doing so the ASP.NET compiler is more self contained and can produce more modular output than ASP.NET 1.x was able to accomplish. This feature makes it possible for Visual Studio to provide an easy model for creating or changing an ASP.NET page or control and immediately being able to run that page or control without first having to recompile it. The new compilation model is used both at runtime as well as by Visual Studio which uses it to dynamically compile pages and provide Intellisense information about the pages and controls you’re working on. The ASP.NET CompilerThe key feature in compilation is the new ASP.NET pre-compiler that is used to compile Web applications. This pre-compiler is used instead of any explicit compilation using the C# or VB.NET compilers. The compiler is made up of a set of internal APIs in the System.Web assembly as well as a new command line utility called ASPNET_COMPILER.EXE. The ASP.NET compiler manages the compilation process of pages and controls dynamically, and decides how to compile them individually. It parses the content of the site and passes off the compilation of each page/control to the appropriate C# or VB.NET compiler. In fact, in ASP.NET 2.0 it’s possible to mix .NET languages in a single Web project so you can create pages and controls in either C# or VB.NET in the same directory. ASP.NET is smart enough to figure out which language is used and create separate assembly for the C# and VB pages/controls. The ASP.NET 2.0 compiler is much more thorough in compiling pages as it can pick up related resources – specifically the CodeBeside classes (using the CodeFile= attribute discussed a little later) that contain your user code as well as the traditional markup that is stored inside of the ASPX, ASCX, ASHX or MASTER page. This makes it possible for the ASP.NET compile all code at runtime - or more accurately at pre-compile time - without requiring an explicit compilation step by Visual Studio or other development environment. Remember that in ASP.NET 1.x with CodeBehind you had to explicitly compile your CodeBehind classes; in ASP.NET 2.0 this explicit compilation step is no longer necessary as the ASP.NET compiler compiles everything related to the Web project on its own. Your application specific code can go inline of the ASPX page or control, it can go into a CodeBeside partial class, or you can create completely autonomous classes in the APP_CODE folder. The APP_CODE folder is a special folder in an ASP.NET 2.0 project and any non-page or control related source code must go into this folder. The content of APP_CODE is treated like a library project and is compiled into a separate assembly. This assembly is then referenced by all of the page or directory level assemblies that ASP.NET creates from your ASPX/ASCX pages that use any of the classes defined in APP_CODE. By default ASP.NET compiles pages and controls on a per directory level. The compiler takes all pages and controls and master pages of a given language (C# or VB.NET) in a directory and compiles them into a single assembly that contains everything that the page or control requires. If you have multiple directories you will have one assembly for each. By default each directory is compiled into a separate assembly, but the compiler can also create one assembly per page/control. When running in Visual Studio directory level compilation is used and every time a change is made in a page or control the directory level assembly gets recompiled. The APP_CODE folder is a special folder into which you can stick any non page/control code and all classes in the APP_CODE folder are essentially compiled into a single APP_CODE assembly. If a change is made in any files in the APP_CODE folder, the APP_CODE assembly is also recompiled. The APP_CODE assembly is referenced by each of the directory/page assemblies, as are any explicit references that are added to the page via the @Reference, @Import and @Register directives and any external assembly references that are stored in the BIN directory. Because your entire Web application is no longer contained in a single assembly, these explicit directives are often required in order to make content from other directories available to the a page in the current directory. This has a number of implications in terms of being able to reference other pages and controls from a page, which we’ll discuss a little later. As a result of this more full featured and more complex approach of compilation, the compiler can completely handle site compilation on its own. If you take all of your ASP.NET ASPX/ASCX/MASTER Pages and .cs or vb.net files and copy them to the server, ASP.NET 2.0 will compile everything completely at runtime on the server and execute the site. This was not possible in ASP.NET 1.x except when you were using inline code in ASPX pages. This is a very nice feature for development time that makes it very easy to share applications with others and test applications in new installations. However, for real-live deployment scenarios this all-code deployment method is less than optimal and ASP.NET also supports pre-compiling of your Web site including of pre-compilation of the ASPX pages. Pre-compilation is a separate step that creates a copy of your Web site or development Web site and compiles the site into a ready-to-deploy installation. The scope of pre-compilation depends on the options chosen which can range from no pre-compilation to pre-compiling both code and ASPX pages using the ASPNET_COMPILER.EXE command line utility. There are many different compilation options and we’ll come back to this later in the article when we talk specifically about deployment. At this point you have a high level view of how the compilation process works, so let’s dig a little deeper into the actual page compilation mechanisms available. Page ParsingThe 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 can be compiled and then executed. At a very high level ASP.NET turns the ASPX page with its HTML markup, control definitions and script content into a class that can be executed. This process varies depending on the mechanism used to set up your Web pages, using either Inline ASPX page code or the CodeBeside model. 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 your user code can be stored 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 any special inheritance mechanism. Instead ASP.NET only 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 being where user code is applied. In the CodeBehind and CodeBeside model ASP.NET uses a base class that is created from your user code. Inline pages on the other hand inherit directly from System.Web.UI.Page and have ALL code generated directly into this single class. Let’s look at a 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. Listing 1: A simple inline ASPX page
<%@
Page
Language="C#"
%>
<script
runat="server">
protected
string
CustomProperty =
"Very
custom";
protected
void
btnSayIt_Click(object
sender,
EventArgs
e)
{
this.lblMessage.Text
=
"Hello, " +
this.txtName.Text
+ ".
" +
}
</script>
<html>
<head
runat="server" id="hdHtmlHeader">
<title>Inline
Compilation</title>
<link
href="WestWind.css"
rel="stylesheet" type="text/css"
/>
</head>
<body>
<form
id="Form1"
runat="server">
<div>
<h1>Inline
Compilation</h1>
<div
style="margin:25px">
What's your name:
<asp:TextBox
runat="server" ID="txtName"></asp:TextBox>
<hr
/>
<asp:Label
runat="server" ID="lblMessage"></asp:Label>
</div>
</div>
</form>
</body> </html> Figure 2 shows the layout of the generated class in .NET Reflector, which is a decompiler that lets you see the class structure and decompiled 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 __Build 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 (<compilation debug="true" /> in web.config) by looking in your Tempoary ASP.NET Files folder in the .NET Framework directory. On my machine the path looks something like this: C:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\compilationanddeployment\fc448eb9\60feb83a 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 looking at the content of files to find the right directory and files. In this directory you will find the compiled DLLs for the APP_CODE assembly as well as any directory level page and control assemblies that you can inspect with Reflector as shown in Figure 2. Also in this directory will be a set of .cs or .vb files that contain the generated ASP.NET classes that ASP.NET uses to compile the assemblies. The names for these assemblies and source files are random based on a hashcode so you have open them individually to find the one you’re are interested in. (Note the source files are available only when you run the site in debug mode) If you look at the .cs file for the above 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 gets constructed. Listing 2 – Excerpt of the ASP.NET generated code to build the Page Parse Tree
protected
override
void
FrameworkInitialize() {
base.FrameworkInitialize();
this.__BuildControlTree(this);
base.AddWrappedFileDependencies(inlinecompilation_aspx.__fileDependencies);
base.Request.ValidateInput(); }
private
void
__BuildControlTree(inlinecompilation_aspx __ctrl) {
this.InitializeCulture();
accessor1.AddParsedSubObject(
HtmlHead
head1 =
this.__BuildControlhdHtmlHeader();
accessor1.AddParsedSubObject(head1);
accessor1.AddParsedSubObject(
HtmlForm
form1 =
this.__BuildControlForm1();
accessor1.AddParsedSubObject(form1);
accessor1.AddParsedSubObject( }
private
HtmlForm
__BuildControlForm1() {
HtmlForm
form1 =
new
HtmlForm();
this.Form1
= form1;
form1.ID =
"Form1";
IParserAccessor accessor1 = form1;
accessor1.AddParsedSubObject(new
LiteralControl("<div>\r\n<h1>Inline
Compilation</h1>\r\n
\r\n
<div style=\"margin:25px\">\r\n
What's your name:
"));
TextBox
box1 =
this.__BuildControltxtName();
accessor1.AddParsedSubObject(box1);
Button
button1 =
this.__BuildControlbtnSayIt();
accessor1.AddParsedSubObject(button1);
accessor1.AddParsedSubObject(new
LiteralControl("\r\n
<hr />\r\n
"));
Label
label1 =
this.__BuildControllblMessage();
accessor1.AddParsedSubObject(label1);
accessor1.AddParsedSubObject(new
LiteralControl("\r\n
</div>\r\n
</div>\r\n
"));
return
form1; }
private
Button
__BuildControlbtnSayIt() {
Button
button1 =
new
Button();
this.btnSayIt
= button1;
button1.ApplyStyleSheetSkin(this);
button1.ID =
"btnSayIt";
button1.Text =
"Say";
button1.Click
+= new
EventHandler(this.btnSayIt_Click);
return
button1; } At the highest level is the FrameworkInitialize method which is called when the page class is instantiated. This method handles ‘housekeeping’ functionality for the page, such as managing file dependencies which determines what related pages/control references are pulled in for compilation and assembly referencing. It also handles validating the safety of request input (unless ValidateRequest="false"). But most importantly it fires of 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 a several literal sections
that are static HTML text. This static text is turned into Literal
controls which are added to the control tree. Individual __BuildXXX
methods for each server control return an instance of a fully configured
child control, which are then added to the container’s
Controls collection via the
AddParsedSubObject method. For the page it’s the Literal controls from
the static HTML and typically the Html Header and Form controls which
are the top level containers.
The same logic is then applied to each of the containers. Each container in turn 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 __Build methods for each of the controls defined in the form, so the TextBox, Button and Label controls are added by referencing their respective __Build methods. You can also define class level code inside of <script runat="server">tags of the markup. Any code that is coded inside of the <script> tag is placed at the top of the class and essentially adds to the classes prototype. Using this mechanism you can add fields, properties, events and methods – anything that you would normally do to add members to a class. Typically your event handling methods are defined in this <script> block as are any custom field/property definitions as shown in Listing 1. <script runat="server"> is most common in Inline pages, but it works for any ASP.NET markup to add code to the generated class. <%= %> and <% %> Script Tags complicate mattersLike <script runat="server">the <% %> tags allow you inject code into the generated page class that ASP.NET creates as part of the compilation process. The server <script> tag is a class level insertion point, while the <% %> tags are a Render method insertion point. <% %> tags are executed by ASP.NET at Render time so special consideration needs to be given to any script tags embedded into the page. Script code also can be fairly complicated as it can intermix with static and control 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"
<%=
x.ToString() %><br
/>
<%} %> </asp:Panel>
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 so by using SetRenderMethodDelegate on the container and creating a custom rendering method that handles this code scenario as shown in Listing 3. Listing 3 – Generated code for containers with <% %> script code uses custom rendering
private
Panel
__BuildControlpanelScript() {
Panel
panel1 =
new
Panel();
this.panelScript
= panel1;
panel1.ApplyStyleSheetSkin(this);
panel1.ID =
"panelScript";
Label
label1 =
this.__BuildControllblMessage();
panel1.AddParsedSubObject(label1);
return
panel1; }
private
void
__RenderpanelScript(HtmlTextWriter
__w,
Control
parameterContainer) {
__w.Write("\r\nWhat's going on here:\r\n");
for (int
num1 = 0; num1 < 10; num1++)
{
parameterContainer.Controls[0].RenderControl(__w);
__w.Write("\r\n
");
__w.Write(num1.ToString());
__w.Write("<br
/>\r\n");
} } Rather than building up the control tree literal controls, ASP.NET only adds any server controls to the control tree. To handle the literal content and the script markup a custom rendering method is generated. This method then explicitly writes out any static HTML content and any script expressions using an HTML TextWriter. Any script code (<% %>) is generated as raw code of the method itself. Because of this hard coded mechanism, ASP.NET does not allow adding any controls to the container if any <% %> tags are assigned to the container. If you’ve ever received this error: The Controls
collection cannot be modified because the control contains code blocks
(i.e. <% ... %>). you now know the reason. Because the method that renders the container with the script tags is hard coded and uses hard coded indexes to any controls referenced, adding any new controls would not work correctly as the indexes of any added controls would only throw off the hardcoded index used by the generated method.
The CodeBeside ModelThe Inline page parsing described above parses a single ASPX/ASCX/MASTER markup file into a single class and creates a single assembly from it. The CodeBeside model is a specialization of the Inline model which breaks out delegation of page or control operation into two distinct classes. Rather than the single class that Inline pages use, CodeBeside has two classes: One class that contains your user code and the ASP.NET control definitions known as the CodeBeside class, and the generated class that contains the control tree generation code that inherits from this class. Figure 3 shows an overall view of how the CodeBeside model works.
Figure 3 – The CodeBeside model uses a partial class to implement user code which is merged with a generated partial class that contains control declarations. The combined class then becomes the base class that the generated ASP.NET control tree class inherits from. The advantage of this two class model is that you can separate your user interface (the markup in the ASPX) and your application logic (your .cs or .vb file) into separate files that are edited separately. For example this makes it easier to hand off ASPX pages or controls to designers who should see as little as possible of the server code that drives the page. The base CodeBeside class is actually made up of two partial classes: One contains your user code, the other is generated by ASP.NET at compile time and contains the control property definitions. The ASP.NET compiler creates the control definitions partial class and compiles it together with your user code class to create the CodeBeside base class. ASP.NET then creates the control tree class as described earlier with the difference that the generated class doesn’t create the control property definitions. Instead it inherits them from the CodeBeside class. In this scenario the controls are defined in the base CodeBeside class, but all the assignments for property values and event hookups are done as part of the control tree class in the various control __Build methods. Both classes are tightly coupled together. ASP.NET then compiles both classes into the same assembly. Pages created for CodeBeside use the CodeFile= attribute on the @Page element to tell ASP.NET that it has to find and compile a CodeBeside class. The syntax for this looks like this:
<%@
Page
Language="C#"
CodeFile="DataEntry.aspx.cs"
Inherits="DataEntry"
%> You need to specify the path to the CodeBeside file and the fully qualified class name. For demonstration purposes let’s use the simple ASPX page code defined in Listing 4 as an example. Listing 4 – A sample CodeBeside Page that splits Html and user code
<%@
Page
Language="C#"
AutoEventWireup="true"
CodeFile="DataEntry.aspx.cs"
Inherits="DataEntry"
EnableTheming="false"%>
<%@
Register
Assembly="westwind.web.controls"
Namespace="Westwind.Web.Controls"
TagPrefix="ww"
%>
<%@
Register
Namespace="CustomControl"
TagPrefix="Custom"
%> <html> <head
runat="server"
id="Header">
<title>Sample
CodeBeside Page</title>
<link
href="WestWind.css"
rel="stylesheet"
type="text/css"
/> </head> <body>
<form
id="form1"
runat="server">
<div>
<h1>
Data Entry Form</h1>
<br
/>
<ww:wwErrorDisplay
runat="server"
id="ErrorDisplay"
DisplayTimeout="5000" />
<br
/>
Enter your Name:
<asp:TextBox
ID="txtName"
runat="server"></asp:TextBox>
<asp:Button
ID="btnSayHello"
runat="server"
OnClick="btnSayHello_Click"
Text="Go"
/>
<br
/>
<hr
/>
<Custom:CustomControl
runat="server"
ID="customHello"
/>
</div>
</form> </body> </html> The page is super simple but I’ve added a couple custom controls to it. One is a control that is defined in this project (CustomControl) and one is an external control in a separate assembly (Westwind.Web.Controls). The DataEntry.aspx.cs CodeBeside class for the markup shown in Listing 3 is defined as follows: public partial class |