Compiling Aspnet Websites
Compiling Aspnet Websites
Extreme ASP.NET
Codebehind and Compilation in ASP.NET 2.0
Fritz Onion
Contents
Codebehind
Compilation
Assembly Generation
Conclusion
As I write this column, the release candidates of the Microsoft® .NET Framework 2.0 and Visual Studio® 2005 have just
come out, and by the time you read this, they will both already be on the shelves. It feels like it's been a long time coming.
I remember sitting in a room on the Microsoft campus in August of 2003 listening to Scott Guthrie and others (including my
fellow columnist, Rob Howard) present the wide array of new features coming in ASP.NET 2.0. They astounded us with one
demo after another of features that greatly simplified Web development, and did so in a pluggable and extensible fashion so
that changes could be made at any level as needed during the development process.
Quite a bit has changed in the subsequent beta releases, mostly in the form of refinements, bug fixes, and control additions.
However, one feature—the codebehind model—has changed rather dramatically since that first preview, primarily in response
to customer feedback. Now on the cusp of the release, I thought I would take this opportunity to describe this new
codebehind model, the rationale behind it, and how you as a Web developer will use it. I will also cover some of the
potentially unexpected side effects of this model and how to plan for them in your designs. Note that the ASP.NET 2.0
runtime fully supports the 1.x model, so applications written for 1.x can run without modification.
Codebehind
Although the codebehind model is different in 2.0, its syntax has changed little. In fact, the change is so subtle that you may
not even notice it unless you look really closely. Figure 1 shows the new codebehind syntax.
Figure 1 Syntax in ASP.NET 2.0
Default.aspx
Copy Code
<%@ Page Language="C#" AutoEventWireup="true"
Default.aspx.cs
Copy Code
namespace MsdnMag
There are two differences between this model and the previous 1.x model—the introduction of the CodeFile attribute in the @
Page directive and the declaration of the codebehind class as a partial class. As you start building the page, you will notice
another difference—server-side controls no longer need to be explicitly declared in your codebehind class, but you still have
complete access to them programmatically. For example, the form in Figure 2 has several server-side controls that are used
programmatically in the codebehind file, but notice the absence of any explicit control declarations in the codebehind class.
Figure 2 Implicit Server-Side Control Access
Default.aspx
Copy Code
<%@ Page Language="C#" AutoEventWireup="true"
<html >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<div>
</div>
</form>
</body>
</html>
Default.aspx.cs
Copy Code
namespace MsdnMag
The reason this works has to do with the partial keyword applied to your codebehind class. In addition to turning your .aspx
file into a class definition with methods for rendering the page, as it has always done, ASP.NET now also generates a sibling
partial class for your codebehind class that contains protected control member variable declarations. Your class is then
compiled together with this generated class definition and used as the base class for the class generated for the .aspx file.
The end result is that you essentially write codebehind classes the way you always have, but you no longer have to declare
(or let the designer declare for you) member variable declarations of server-side controls. This was always a somewhat
fragile relationship in 1.x, since if you ever accidentally modified one of the control declarations so that it no longer matched
the ID of the control declared on the form, things suddenly stopped working. Now the member variables are declared
implicitly and will always be correct. Figure 3 shows an example set of classes involved.
Figure 3 Class Generation with Codebehind
...
...
Note that the partial class model is only used if you use the CodeFile keyword in your @ Page directive. If you use the
Inherits keyword without CodeFile (or with the src attribute instead), ASP.NET resorts to the 1.x codebehind style and simply
places your class as the sole base class for the .aspx file. Also, if you have no codebehind at all, the class generation acts
very much the same as it does in 1.x. Since ASP.NET 2.0 is backwards compatible with 1.x, there is now a range of
codebehind options at your disposal.
Visual Studio 2005 will use the new partial class codebehind model for any Web Forms, and it will also happily convert Visual
Studio .NET 2003 projects to use the new model as well if you use the conversion wizard. It is best, if possible, to convert all
files to the new codebehind model, since some of the new features of ASP.NET 2.0 depend on it (if you're using Visual
Studio, converting is pretty much the only option, since Visual Studio 2005 won't open unconverted 1.x projects). For
example, strongly typed access to the Profile property bag is added to the sibling partial class for codebehind classes in 2.0,
but if you use the 1.x codebehind model, that strongly typed accessor is added directly to the .aspx generated class
definition, and will be unavailable to your codebehind class. This is also true for strongly typed Master Page and previous
page access.
Compilation
At this point, you may be wondering why the ASP.NET team bothered to use inheritance at all with this new codebehind
model. ASP.NET could easily generate all of the control variable declarations in addition to the rendering methods from the
.aspx file as a partial class which could then be merged with your simplified codebehind class. This is exactly how Windows
Forms works in the .NET Framework 2.0. All of the designer-generated code is placed into a sibling partial class which is then
merged with your application logic and event handlers into a single Form-derived class, creating a clean separation between
machine-generated code and developer code without resorting to inheritance.
Well, it turns out that the original implementation of codebehind in ASP.NET 2.0 did exactly this—the codebehind class was
just a partial class that was merged with the parsed .aspx file class definition. It was simple and effective, but unfortunately,
not flexible enough. The problem with this model was that it was no longer possible to deploy the codebehind files in
precompiled binary assemblies along with intact .aspx files since they now had to be compiled at the same time (a restriction
when using partial classes is that all partial pieces of a class must be merged during a single compilation, and class
definitions cannot span assemblies). This restriction was unacceptable to many developers as they were already used to
being able to deploy binary codebehind assemblies along with intact .aspx files which could then be updated in place without
having to recompile. This is, in fact, the exact model used by default in Visual Studio .NET 2003, and is thus very prevalent
in practice.
As a result of reintroducing the inheritance model and shifting the partial class into the base class, .aspx files can now be
deployed and compiled independently from the codebehind class. To complete the picture, you need some way to generate
the sibling partial classes containing control variable declarations during compilation or deployment since this was always
done in the past on demand in response to requests. Enter the ASP.NET compiler.
The ASP.NET compiler (aspnet_compiler.exe) was originally introduced in ASP.NET 2.0 as a way of completely precompiling
an entire site, making it possible to deploy nothing but binary assemblies (even .aspx and .ascx files are precompiled). This
is compelling because it eliminates any on-demand compilation when requests are made, eliminating the first
postdeployment hit seen in some sites today. It also makes it more difficult for modifications to be made to the deployed site
(since you can't just open .aspx files and change things), which can be appealing when deploying applications that you want
to be changed only through a standard deployment process. The compiler that ships with the release version of ASP.NET 2.0
supports this binary-only deployment model, but it has also been enhanced to support an updatable deployment model,
where all source code in a site is precompiled into binary assemblies, but all .aspx and .ascx files are left basically intact so
that changes can be made on the server (the only changes to the .aspx and .ascx files involve the CodeFile attribute being
removed and the Inherits attribute being modified to include the assembly name). This model is possible because of the
reintroduction of inheritance in the codebehind model, so that the sibling partial classes containing control declarations can
be generated and compiled independently of the actual .aspx file class definitions.
Assembly Generation
Now that compilation into assemblies can happen in one of three places (either explicitly by the developer, using
aspnet_compiler.exe, or during request processing), understanding the mapping of files into assemblies becomes even more
important. In fact, depending on how you write your pages, you can actually end up with an application that works fine when
deployed as all source or all binary, but which fails to compile when deployed using the updatable switch.
The model ASP.NET generally uses creates separate assemblies for the contents of the App_Code directory as well as the
global.asax file (if present), and then compiles all of the .aspx pages in each directory into a separate assembly. (If pages in
the same directory are authored in different languages or if they have dependencies on each other through an @ Reference
directive, they could also end up in separate assemblies.) User controls and Master Pages are also typically compiled
independently from .aspx pages. It is also possible to configure the App_Code directory to create multiple assemblies if, for
example, you wanted to include both Visual Basic® and C# source code in a project. There are some subtleties in the details
of assembly creation, depending on which mode of deployment you have chosen. Figure 6 describes the components of your
Web site that compile into separate assemblies based on the deployment mode you are using. (Note that I am ignoring the
resource, theme, and browser directories since they don't contain code, although they are compiled into separate assemblies
as well. The target assembly can also differ based on language variance and reference dependencies, as mentioned
previously.)
Figure 6 Assembly Generation
Deployment Mode
When it's Request time Deployment time (R) = Compiled at request time
compiled (D) = Compiled at deployment
time
The only other twist in the assembly generation picture is that you can use the -fixednames option of aspnet_compiler to
request that each .aspx file be compiled into a separate assembly whose name remains the same across different invocations
of the compiler. This can be useful if you want to be able to update individual pages without modifying other assemblies on
the deployment site. It can also generate a large number of assemblies for sites of any significant size, so be sure to test
your deployment before depending on this option.
If this sounds complicated, the good news is that most of the time you shouldn't have to think about which files map to
separate assemblies. Your .aspx files are always compiled last, and always include references to all other generated
assemblies, so typically things will just work no matter what deployment model you choose.
One of the key differences in deployment that may actually affect the way you author code in your pages is the split in
compilation when using updatable deployments. When you deploy an updatable site, the codebehind files are compiled into
separate assemblies prior to deployment. The classes generated from the .aspx files are not compiled until a request is
actually made for a file in a directory. This is in contrast to binary deployment, in which all files are compiled prior to
deployment, and to source deployment, in which all files are compiled at request time. As a simple example of how this can
cause problems, consider the user control (.ascx file) in Figure 7 with an embedded property, and an associated page that
uses the control and sets the property from its codebehind class.
Figure 7 User Control
MyUserControl.ascx
Copy Code
<%@ Control Language="C#"%>
<script runat="server">
_mainPanel.BackColor = System.Drawing.Color.FromName(Color);
base.OnLoad(e);
</script>
<h2>Some text</h2>
Other text
</asp:Panel>
Default.aspx
Copy Code
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<div>
</uc1:MyUserControl></div>
</form>
</body>
</html>
Default.aspx.cs
Copy Code
using System;
using System.Web.UI;
MyUserControl1.Color = "purple";
The page in Figure 7 will compile and run in either source or binary deployment mode, but will fail to compile when deployed
as an updatable site since the definition of the Color property of the user control is unavailable at deployment time (this
limitation also existed in the 1.x model). You can typically avoid issues like this by keeping all code in codebehind files or, at
the other extreme, not using codebehind files at all and leaving code directly in .aspx and .ascx files.
Another thing to keep in mind when considering the file-to-assembly mapping is that the use of the internal keyword to
prevent external assemblies from accessing methods in your classes may work in some deployment scenarios and not
others, because of the different assembly mapping options. Unless you plan ahead of time which deployment option you will
be using, it is probably best to avoid internal methods in your pages and stick to the type-scoped protection keywords:
public, protected, and private.
Conclusion
The new codebehind model in ASP.NET 2.0 seems both familiar and foreign to ASP.NET developers. It's familiar because it
still uses inheritance to relate codebehind classes with their .aspx generated class definitions, and yet foreign elements like
partial classes and the implicit generation of control member variable declarations are fundamental shifts. In practice, you
will probably not notice much difference in usage, but it will be important to understand the class relationships and assembly
mappings outlined here whenever you are doing something out of the ordinary, like creating a common base Page class or
mixing codebehind and inline code models.
Fritz Onion is a cofounder of Pluralsight, a premier Microsoft .NET training provider, where he heads the Web development
curriculum. Fritz is the author of Essential ASP.NET (Addison Wesley, 2003) and the upcoming Essential ASP.NET 2.0
(Addison Wesley, 2006). Reach him at pluralsight.com/fritz.