t’s crucial to understand the changes to the compilation process from ASP.NET 1.x to ASP.NET 2.0 so you can debug your Web applications effectively. This article shows you how compilation works now and what has changed from ASP.NET 1.x.
ASP.NET 2.0’s release offers many welcome changes and additions to the ASP.NET model of Web development. Compilation and deployment has changed drastically in ASP.NET 2.0 and these changes are somewhat controversial. In this article, I’ll look at the stock project model and explain how the different compilation models work. I’ll look at the project system, the page parsing mechanism and page compilation, and how applications deploy. Because developers have raised a number of concerns about stock projects, Microsoft recently released a couple of add-ins for Visual Studio that address some of the shortcomings and complaints. 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.0
Microsoft has tightly linked the new project model in ASP.NET 2.0 with the new compilation and deployment features. They completely overhauled the way that page compilation works in the new version without breaking the way that original compilation worked in ASP.NET 1.1.
The 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 that 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 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 can figure out from the directory structure how to 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 projects is much easier in Visual Studio 2005 simply by selecting a directory in the file system. Once opened, the directory acts as the project, providing the file content for the projectthere’s no explicit project file in Web projects.|
The new project system allows you to open projects from a directory, a local IIS server, 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 is not a big difference from a file-based project. An FTP site opens a remote 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 is pulled into the project.
You can also open a Remote Site, which like Visual Studio 2003, requires installing the FrontPage extensions 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?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 projects that contain lots of static content that you don’t want to automatically include in your project. For example, if you have a root Web site that has subdirectories that are in turn virtual directories, a Remote Site prevents Visual Studio from 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 have a new or existing Web application up and running 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 creating a new application or running an existing one.
Easy on the Surface?Complex Underneath
While project behavior gets easier in ASP.NET 2.0, 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 based purely on simple inheritance, this new model uses run-time control and event generation, partial classes, inferred referencing of assemblies, delayed run-time compilation, and single-page assembly compilation along with considerable help from the ASP.NET runtime and Visual Studio to make it all work.
A lot of magic 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 this inferred type information that logistically wouldn’t be available until run time. 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 provide you with a rich design-time experience with IntelliSense.
Most of the time you don’t need to worry about these internals because they’re encapsulated within ASP.NET itself. However, if you go beyond simple scenarios and run into situations where the simple method just doesn’t work, you have to fully understand all the intricacies of this complex model to make it work for you. This need for understanding affects some developers more than others, depending on the application type. I think developers building and working with reusable and extensible Web frameworks containing lots of generic code will quickly reach the limitations of the new project model.
Compilation in ASP.NET 2.0 works by running the new ASPNET_COMPILER.EXE utility against a Web application. The compiler offers many project compile options, including 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 16 different compilation and pre-compilation combinations?and none of them are likely to be exactly what you want; 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.
The only simple deployment method is in-place deployment?you simply copy your entire development environment, including source code, to the server. All the other options require that you delete files on the server and then recopy newly compiled 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 the Web Deployment Projects (WDP) add-in, which provides a mechanism to post-process the output from the ASPNET_COMPILER.EXE and create a single assembly.
How Things Work in ASP.NET 1.x
If you’re like me, you probably come from an ASP.NET 1.x background and you’re very familiar with that model. To put things in perspective, let’s first review how things work in ASP.NET 1.x and Visual Studio 2003.
ASP.NET 1.1 uses a CodeBehind model 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 run time. So there are two classes: One class contains your user code, the control definitions, as well as event hookups. At run time, ASP.NET generates the second class that contains the page parse tree, which is a code representation of all the HTML markup and control definitions in the ASPX page.
The CodeBehind base class includes control definitions, event hookups, and of course page-specific code, which handles page-level events such as Page_Load and event triggers such as button clicks or change events. Visual Studio generates control definitions and event hookups at design time. This has been a sore point in Visual Studio 2003 because it occasionally mangles the event hookups, mysteriously losing events you had previously mapped to page handlers. Then, when you run the application for the first time, 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, which is why it’s called a parse tree. This tree can potentially be many levels deep depending on the page control hierarchy. The generated class inherits from your CodeBehind class; your control definitions and event handling code are accessible to this class. The additional code generated is responsible for rendering the page.
Visual Studio (or the command-line compilers) explicitly handles the compilation of all the CodeBehind code in ASP.NET 1.x, which creates a single assembly from all your CodeBehind code of all pages, controls, and classes defined in the project. On the other hand, any markup pages (ASXP/ASCX/ASHX etc.) always parse and compile at run time. 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 pages, 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 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.
Single Page, Inline Markup Pages
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 and all the required script code, placed between code markup (<% %>) or tags. In this page model, all compilation occurs at run time and ASP.NET parses the single page into the control tree class directly inherited from System.Web.UI.Page. The class contains embedded user code inside script tags, which ASP.NET parses into the appropriate class areas. Server tags become class members, so event handler methods, custom methods, and property definitions are created inside server