ASP.NET 2.0 Internals

 

March 2005
Updated June 2006

Jayesh Patel, Bryan Acker, Robert McGovern
Infusion Development

Summary: Although ASP.NET 2.0 is 100-percent backward compatible with ASP.NET 1.1, ASP.NET 2.0 brings a number of internal changes to ASP.NET. This article outlines changes to the code model, compilation, page lifecycle, and more. (20 printed pages)

Contents

Introduction
Code Model
Compilation
Page Lifecycle
Extensibility
Advanced Caching Techniques
Performance
Summary

Introduction

For professional ASP.NET developers, the big questions about ASP.NET 2.0 relate to what has changed on the inside. New features are fun and interesting to learn about, but changes to the core structure of ASP.NET speak louder to developers who really want to master the technology. In this white paper, we will cover how the internal structure of ASP.NET 2.0 has changed since version 1.x.

The topics covered in this paper will be of use to performance minded developers and technical architects seeking to fine tune applications. Specifically, we will examine key areas of the code model, compilation, page lifecycle, extensibility, caching and performance enhancements.

Many examples in this document require considerable familiarity with ASP.NET, Visual Basic .NET and/or C# syntax. Where applicable, reference documents have been provided for in-depth discussion on particular subjects.

Code Model

Perhaps the most obvious change in ASP.NET 2.0's internal workings relates to how an ASP.NET Web page is created. In this section, we will examine the changes to the code behind model and how these changes impact ASP.NET development.

Coding Models in ASP.NET 1.x

In ASP.NET 1.x, developers had two primary options for developing a Web Form. First, the developer could follow the traditional ASP model and write code directly in the ASPX page. This process, known as code inline, works very well for simple commands. However, for more complex code, writing code inline results in difficult to read Web pages that mix presentation (HTML) with functionality (code).

In ASP.NET, the default coding practice was changed to help solve this problem. Business logic and event handling code could be written in a separate, code only file known as the code behind file. The code behind model links a code only file with the ASPX file that contains presentation tags. By separating the code from the presentation, development teams could work faster by allowing the designers to work on the presentation file while the developers worked on the code file.

ms379581.internals_fig01(en-US,VS.80).gif

Figure 1. ASP.NET 1.x Coding Model

The primary difficulties with the code behind model related to the way in which the code behind file had to be synchronized with the ASPX page. Although the ASPX page inherited from the code behind file in a programming sense, the two files were actually coupled by a more complex relationship.

For more detailed information on the code behind model in ASP.NET 1.x, see the MSDN Library article, Web Forms Code Model.

Inheritance Complexity

The design paradigm for ASP.NET was that developers would use Visual Studio .NET to drag and drop controls onto the ASPX page. Visual Studio would then automatically generate the appropriate supporting code in the code behind file. If controls were added to the ASPX page, new code had to be added to the code behind file. In other words, despite the inheritance relationship pointing the other way, the ASPX page actually drove the design of the code behind file.

Compilation Complexity

The second synchronization problem was in the way that the files were compiled. All of the code behind files, along with any supporting classes, were compiled into an assembly and stored in the /bin directory of the Web application. The compilation step occurred prior to deploying the application. On the other hand, the ASPX page was compiled at runtime the first time the page was requested. The ASP.NET runtime actually compiled the ASPX page into its own temporary assembly.

In this process the ASPX page could be changed without updating the code behind assembly. That is, a developer might choose to modify a property or change the type of a control on an ASPX page after deployment, and the code behind file would not be updated, nor would the application assembly be recompiled. When this happened, the application would suffer unexpected errors because of the mismatch between code behind files and their associated ASPX pages.

Coding Models in ASP.NET 2.0

ASP.NET 2.0 continues to offer both the code inline and code behind coding models. In terms of the code-inline model, very little has changed except for the way that Visual Studio 2005 supports single-file development. Specifically, ASPX pages now have much better IntelliSense support. That is, when you start writing code inside <% %> brackets, Visual Studio will provide full IntelliSense support for code completion, help and code insertions.

ASP.NET 2.0 addresses both the inheritance and compilation concerns of the code behind model by modifying the nature of the code behind file. In ASP.NET 2.0, the code behind file is no longer a full implementation of the System.Web.UI.Page class. Instead, the code behind file is a new construct called a partial class. The partial class contains all of the user-defined code, but omits all of the plumbing and connectivity code that was auto-generated by Visual Studio .NET in ASP.NET 1.x. When an ASPX page with a new code behind file is requested, the ASP.NET 2.0 runtime will actually combine the ASPX page and the partial class into a single class, rather than two separate classes.

ms379581.internals_fig02(en-US,VS.80).gif

Figure 2. The code behind model in ASP.NET 2.0

The partial class uses a new keyword (Expands in Visual Basic, or Partial in C#) to indicate that the code in the class should be merged with another class at runtime. Similarly, the ASPX page uses a new directive called compilewith to indicate its association with the code behind file. The end result is always one compiled code file in a single assembly rather than two files in two different assemblies.

Comparing Code Behind Files

If you are familiar with traditional ASP.NET 1.x code behind files, you know that Visual Studio inserts auto generated control declarations and initialization code. This auto-generated code is a direct result of the bi-directional synchronization between the code behind file and the ASPX file. A typical ASPX page with a label would have a corresponding code behind file consisting of many lines of auto-generated text:

namespace WebApplication1
{
  public class WebForm1 : System.Web.UI.Page
  {
    protected System.Web.UI.WebControls.Label Label1;
    private void Page_Load(object sender, System.EventArgs e) {    }
    #region Web Form Designer generated code
      override protected void OnInit(EventArgs e)
      {
        InitializeComponent();
        base.OnInit(e);
      }
      private void InitializeComponent()
      {    
        this.Load += new System.EventHandler(this.Page_Load);
      }
      #endregion
  }
}

Listing 1. A code behind file in ASP.NET 1.x

The auto-generated code not only defines the label (bold line), it also declares a new event (Load) and automatically wires the event to an auto-generated method wrapper (Page_Load()).

For each code behind .aspx file you create in ASP.NET 2.0, ASP.NET generates a much cleaner partial class that is merged with the partial class you define in your code behind file. These two partial classes merge into a single class definition at compilation that serves as the base class to the class generated from your .aspx file.

namespace ASP {
public partial class Webform1 : System.Web.UI.Page {
  protected void Page_Load(object sender, EventArgs e)
    {   
    }
}
}

Listing 2. A code behind file in ASP.NET 2.0

The developer can access Label1 automatically and can add events as needed. For example, a Page_Load event can be added to initialize the label:

Namespace ASP{
public partial class Webform1 : System.Web.UI.Page {
    protected void Page_Load(object sender, EventArgs e)
    {
        Label1.Text = "Hello ASP.NET 2.0";
    }
}}

Listing 3. Adding events in the new code behind file

The event syntax can be generated through Visual Studio 2005. The resulting code behind file is much shorter and free from any auto-generated code. The ASP.NET runtime automatically wires the events in the code behind to the controls in the ASPX. In other words, the ASP.NET runtime now automatically performs the code generation that was performed by Visual Studio.

The benefit of the new two-step compilation is that it allows you to compile the code-behind class into a binary and then support deploying the .aspx file as html source for later modification. You could not deploy compiled code-behind and modify the .aspx html source later, as the .aspx source was the partial type that the code-behind was compiled with.

Inheritance Complexity

The new code behind model greatly reduces the inheritance complexity. Because the ASPX page does not directly inherit from the code behind file, the code behind file no longer needs to define and support all of the controls defined on the ASPX page. Similarly, the code behind file can automatically access any of the controls on the ASPX page without requiring the declaration code that was required in ASP.NET 1.x. All of this is possible because the ASP.NET runtime automatically inserts the required declaration and event wiring code into the final compiled file. Because the runtime takes on that responsibility, neither the code developer nor the Web developer needs to worry about it.

During design time, the linkage is maintained by Visual Studio 2005. The Visual Studio environment leverages the ASP.NET runtime compilation piece to ensure that the code developer and Web developer can work in synchronization.

Compilation Complexity

Because the new code behind file is joined to the ASPX page and compiled into a single complete class at runtime, the compilation complexity disappears. That is, the code behind file is automatically synchronized with the ASPX page. Even with the new compilation model it is still possible to have unsynchronized code but the issue can be quickly located because the resulting exception is much more explicit.

Compilation

Thanks to the page model introduced in ASP.NET 1.x, the compilation process for an ASP.NET Web page has always been divided into two phases. First, the code behind files and any other supporting classes are compiled into an assembly and then the individual ASPX files are compiled at runtime. Although this model has many advantages, it has a few drawbacks. ASP.NET 2.0 offers several alternatives to the basic model, providing a wider range of compilation options depending on your specific needs.

Compilation in ASP.NET 1.x

The primary compilation model in ASP.NET 1.x resulted in one application assembly (containing all of the compiled code behind files and other source code) and one temporary assembly created for each ASPX page that was requested. In some cases, compiler optimizations such as batching may cause the temporary ASPX pages to be compiled into the same assembly. In either case, each ASPX page is compiled into a temporary assembly so that it can be loaded into the ASP.NET runtime.

ms379581.internals_fig03(en-US,VS.80).gif

Figure 3. Compilation in ASP.NET 1.x

Although this model has advantages, it also has two primary disadvantages. First, the ASPX pages must be deployed to the Website in human readable form. If your developers used the code inline model, that means that some or all of your business logic may also be deployed on a production server. Although IIS and ASP.NET are configured not to expose the raw ASPX pages, a clever attacker could still access the files through any exploit that opened up access to the Web server. Second, the first time anyone requests a Web page, the response will be slower than normal, as the ASP.NET runtime has to compile the ASPX page.

The only control a developer has over this process is whether or not to batch compile the ASPX pages. In ASP.NET 1.x, you can configure batch compilation in the Web.config file by modifying the <compilation> tag:

<compilation  
  batch="true|false"
  batchTimeout="number of seconds"            
  maxBatchSize="maximum number of pages per batched compilation"
  maxBatchGeneratedFileSize="maximum combined size (in KB) of the 
   generated source file per batched compilation"          
</compilation>

Listing 4. Configuring Batch Compilation

Batch compilation trades start up time for reduced load time on the first request of a Web page. The other benefit of batch compilation is that all of the ASPX files are compiled into a single temporary assembly rather than one temporary assembly per page.

Compilation in ASP.NET 2.0

ASP.NET 2.0 offers four different compilation models for a Web application:

  1. Normal (ASP.NET 1.x)—In a normal ASP.NET Web application, the code behind files were compiled into an assembly and stored in the /bin directory. The Web pages (ASPX) were compiled on demand. This model worked well for most Websites. However, the compilation process made the first request of any ASP.NET page slower than subsequent requests. ASP.NET 2.0 continues to support this model of compilation.
  2. Deployment pre-compilation—A new feature of ASP.NET 2.0 allows for full compilation of your project prior to deployment. In the full compilation, all of the code behind files, ASPX pages, HTML, graphics resources, and other back end code are compiled into one or more executable assemblies, depending on the size of the application and the compilation settings. The assemblies contain all of the compiled code for the Website and the resource files and configuration files are copied without modification. This compilation method provides for the greatest performance and security at the cost of removing all ability to modify the Website post-deployment. If you are working with highly visible or highly secure Websites, this option is the best choice for final deployment. However, if you are building a small site running on your local intranet, and the site changes frequently, full pre-compilation may be over kill.
  3. The ASP.NET 2.0 compilation model also allows you to pre-compile all of the code behind files for you your application and still make updates to your code. You can compile your code-behind and original .ASPX files (both partial classes) into a single pre-compiled class (the page's base class). If you chose to edit your .aspx file at run-time, you must simply recompile your page.
  4. Full runtime compilation—At the other extreme of deployment pre-compilation, ASP.NET 2.0 provides a new mechanism to compile the entire application at runtime. That is, you can put your un-compiled code behind files and any other associated code in the new \code directory and let ASP.NET 2.0 create and maintain references to the assembly that will be generated from these files at runtime. This option provides the greatest flexibility in terms of changing Website content at the cost of storing un-compiled code on the server.

Choosing the best compilation option will depend on your exact circumstances and needs. However, the compilation model remains flexible. Even if you choose to make use of the \code directory to store your code behind files, you may still deploy your application using the full compilation method.

Batch Compilation

In ASP.NET 2.0, you can batch compile any application with a single URL request. As with ASP.NET 1.x, batch compiling removes the delay on the first page request, but creates a longer cycle time on startup. In addition, batch compilation still requires that the code behind files are compiled pre-deployment.

The Web.config batch compilation settings still work in ASP.NET 2.0. The advantages of batch compilation are that the pages are immediately available to the first user, and that any errors in the ASPX pages will be detected during the batch compilation. However, batch compilation does add a delay to the application start up and must be built into the Web.config file. It should be noted if a file fails, it is thrown out of the batch.

Deployment Pre-Compilation

Deployment pre-compilation allows you to create one or more assemblies that are an executable version of your Website. The resulting assemblies contain the compiled code for the Website. HTML pages, resources, configuration files, and ASPX pages are copied separately.

Deployment pre-compilation requires the use of a command line utility called aspnet_compiler.exe. This utility creates a target deployment directory that contains a /bin directory with assemblies, and stub files for the various ASPX pages. The utility can also be used to do an in-place pre-compilation, similar to behavior of calling the "magic page". The stub files share the names of your ASPX pages, but contain simple code that calls into the compiled assembly. In other words, the ASPX pages are simply empty shells rather than fully functional pages.

By pre-compiling the Website for deployment, you gain increased security because none of your code can be accessed without decompiling the assembly. For increased protection, you can obfuscate the resulting assembly and make your Web application even more secure. The primary disadvantages of deployment pre-compilation are that you have to perform these steps prior to deployment and that you can't change your Website after it has been deployed. If you want to make changes, you have to recompile the Website and redeploy it.

For most major Web applications, the deployment pre-compilation option will be the preferred mechanism for deployment, as it reduces the amount of raw code deployed on the Web server and offers the best security. The increased process can be built into the normal development-test-deployment cycle without any significant lose of productivity.

Full Runtime Compilation (The \app_code directory)

In all three compilation methods described so far, you have to compile all code files (code behind and supporting classes) prior to deployment. In ASP.NET 2.0, you have the

The \app_code directory is a special directory that holds un-compiled classes. At runtime, the ASP.NET runtime compiles the contents of this directory into an assembly that is automatically referenced by the ASPX pages in the application. Through the Web.config, you can create sub folders that will contain the language specific satellite assemblies. The advantage of the code directory is that you can deploy without fully compiling your project, thereby reducing mismatch potential. The drawback is that you are potentially exposing un-compiled code on your server.

This option works best for ASP.NET applications that do not require large amounts of support code (either in the form of code behind files or external objects). For a simple application, the ability to rapidly deploy and test the system provides several advantages over the more robust compilation methods.

Page Lifecycle

ASP.NET 2.0 offers two major changes in the lifecycle of an ASP.NET page. First, ASP.NET 2.0 exposes new events to support new features including master pages, personalization and the integrated mobile device support. Second, ASP.NET 2.0 introduces cross-page posting for Web forms.

New Events

ASP.NET 2.0 provides a more granular page lifecycle method stack as compared to ASP.NET 1.x. The added methods provide a greater level of control to the Web developer. These events can be accessed through the Page object on any ASP.NET page.

Table 1 shows the comprehensive method list. The method column displays the actual event method name, the Active column indicates whether the event is always active or only active during PostBack actions and new methods added in ASP.NET 2.0 are in bold.

Method Active
Constructor Always
Construct Always
TestDeviceFilter Always
AddParsedSubObject Always
DeterminePostBackMode Always
OnPreInit Always
LoadPersonalizationData Always
InitializeThemes Always
OnInit Always
ApplyControlSkin Always
ApplyPersonalization Always
OnInitComplete Always
LoadPageStateFromPersistenceMedium PostBack
LoadControlState PostBack
LoadViewState PostBack
ProcessPostData1 PostBack
OnPreLoad Always
OnLoad Always
ProcessPostData2 PostBack
RaiseChangedEvents PostBack
RaisePostBackEvent PostBack
OnLoadComplete Always
OnPreRender Always
OnPreRenderComplete Always
SavePersonalizationData Always
SaveControlState Always
SaveViewState Always
SavePageStateToPersistenceMedium Always
Render Always
OnUnload Always

Table 1. Page Life-cycle methods

For example, the new TestDeviceFilter method can be used to determine which device filter is in place, and use this information to decide how to display the page. On the other hand, the new LoadControlState method only fires during a postback. This method can be overridden (in conjunction with SaveControlState) to create alternate serialization schemes for saving and restoring control state during a postback.

Looking at the low level details of the page lifecycle we can see where many of the features available in ASP.NET 2.0 such as themes and personalization would naturally be implemented. For example, a theme would be processed in the IntializeThemes event, whereas personalization data would be loaded in the LoadPersonalizationData and later applied in the ApplyPersonalization method. Note that method order is extremely important with respect to which UI elements will determine the final look and feel of your Web applications.

Cross Page Posting

The other major change in page lifecycle involves post back events and Web Forms. In ASP.NET 1.x, Web Forms automatically postback to their host page. That is, when a user submits a form, the form data is always submitted back to the page containing the original form. This design decision allows for easy storage of control state, but limits the ability of the developer to perform more complex operations.

In ASP.NET 2.0, Web Form controls have a new property that lets the developer decide where the form data should be sent on a submit action. In most cases, the postback mechanism will be desired, and it is therefore still the default. However, if the developer wants to post data to a different form, it is now possible.

ms379581.internals_fig04(en-US,VS.80).gif

Figure 4. Postback and Cross Page Posting

You can, for example, create a multi-page wizard consisting of several different forms. Each form submits to the next page in the sequence until the user reaches a summary page where final validation can occur. Data from the last page can be accessed in the current context via the PreviousPage object. The PreviousPage object stores the validated data from the previous page for use in the current page. Thanks to this object, cross page posting doesn't sacrifice control persistence. If your user needs to back up one form in the sequence, the page data is immediately accessible and the user won't have to re-enter all of the data.

Extensibility

ASP.NET was originally designed as an open framework. That is, many of the modules and components that make up ASP.NET can be extended, modified or replaced to suit your particular needs. In ASP.NET 2.0, the extensible nature of the framework is clearly illustrated with the new HttpHandlers and HttpModules that are now a standard part of the framework.

The Request Pipeline

In ASP.NET, requests are passed from the Web server through an Internet Server Application Programming Interface (ISAPI) filter and on to the actual ASP.NET runtime.

ms379581.internals_fig05(en-US,VS.80).gif

Figure 5. The Request Pipeline

When IIS receives a request, the extension is mapped to an ISAPI filter according to the IIS settings. The extensions .aspx, .asmx, .asd, and others are mapped to the aspnet_isapi.dll which is simply an ISAPI filter that launches the ASP.NET runtime. Once a request hits the ASP.NET runtime, it starts at the HttpApplication object, which acts as the host for the ASP.NET Web application. The HttpApplication object:

  1. Reads the machine and application level configuration files
  2. Passes the request through one or more HttpModule instances. Each HttpModule provides a service such as session maintenance, authentication or profile maintenance. These modules pass the request back to the HttpApplication.
  3. Passes the request to an HttpHandler based on the verb and the path. The verb refers to the HTTP verb used in the request (GET, POST, FTP, etc.) and the path refers to a URL within the application. Depending on how the handlers are configured, the request may be processed as an ASP.NET page (System.Web.UI.Page is an implementation of IHttpHandler), or the request may trigger another action such as batch-compilation of all Web pages (precompilation.asd triggers the PrecompHandler).

In ASP.NET 2.0, this model stays intact. However several new modules and handlers have been added to provide additional services. As with ASP.NET 1.x, you can extend, replace or reconfigure any of the module or handler classes in order to provide your own custom functionality.

New Modules

Naturally, new HttpModules have been added to support the new services offered in ASP.NET 2.0. Specifically, an ASP.NET application with default module settings will include new modules for:

  • SessionID—The session identification mechanism has been split off the ASP.NET 1.x Session module in order to provide greater control over cookies, URL rewriting and other forms of session ID generation.
  • Role Management—A new module has been added for providing role based services in support of the new user identification mechanisms. This module helps link ASP.NET applications to the role based security built into the .NET framework.
  • Anonymous Identification—The new personalization features support anonymous users. This module helps keep track of which features an anonymous user can access and how these features are maintained between requests.
  • Profile Management—The profile module links to the new profile service and helps provide user specific persistent data storage.

In addition to these new modules, the behaviors of some of the older modules have changed. For example the output caching module now supports the new caching techniques described later in this white paper.

New Handlers

In addition to the new modules, ASP.NET 2.0 introduces new handlers to support the application configuration tools and other new features such as the batch compilation request. The most important of the new handlers include the ".axd" family which process Website administration requests. These handlers launch the internal administration tools that allow developers to configure ASP.NET users as well as other settings. The administrative handlers include:

  • Web Administration—The WebAdminHandler is the main page for the administrative Website. This handler provides the starting point for administering ASP.NET 2.0 Web applications.
  • Trace—The ASP.NET 1.x TraceHandler has been improved and is the only "axd" handler that was available in ASP.NET 1.x.
  • Web Resources—Web resources can now be configured post-deployment thanks to the new administrative tool and the WebResourcesHandler.
  • Cached Images—The CachedImageServiceHandler provides support for caching graphical components.
  • Precompile—As mentioned earlier, the PrecompHandler can be used to batch compile all of the ASPX pages in an ASP.NET application.
  • Web Part Export—The WebPartExportHandler provides support for storing and transferring Web Part layouts. Web Parts are a new mechanism for personalizing the look and contents of a portal style Web application.

As always, the HttpForbiddenHandler is linked to any file type that should never be returned. In ASP.NET 2.0, the list of forbidden file types has been expanded to include master pages, skin files and other new developer components.

Advanced Caching Techniques

One way to improve the performance of any Web application is to cache static content in memory. Cached content is always returned faster than freshly rendered content. However, the tradeoff is that cached content may become stale. ASP.NET 1.x supports several kinds of caching including:

  • Page level—Each page may be cached as a whole piece or based on the parameters used to access the page. The cached page expires after a fixed time.
  • Page fragment—If the page was built with user controls (.ascx files), then the user controls could be cached independently of the rest of the page content.
  • Programmatic caching—The developer could also cache objects thanks to the cache API. The cache API offers the distinct advantage of providing a means to create different types of dependencies for when the cache should be flushed.

In ASP.NET 2.0, the page level caching mechanism has been extended to support database dependencies. With a database cache dependency, a cached page can be tied to a particular table in a SQL Server database. If the data in the table changes, the web cache will automatically expire. In addition, a developer can now use post-cache substitution to replace part of the cached content with refreshed content. Post cache substitution allows an application to use page level caching even if part of the page should be dynamically generated.

Database Cache Invalidation

For most data driven Websites, caching can be a thorny topic, especially when a situation demands that need for caching and the necessity up-to-date data. In ASP.NET 1.x, pages could be cached for a length of time and organized by input parameters (querystring or POST parameters):

  <%@ outputcache duration="3600" varybyparam="ProdID" %> 

Listing 5. ASP.NET 1.x Output Cache Directive

For example, the code in Listing 5 caches a page in memory for one hour based on the variable ProdID. The problem that arises in the above example is what to do if relevant business data is updated elsewhere. For example, consider a product catalog page cached by product ID. If information about this product (e.g., quantity available, price) is updated from an administrative site, incorrect data is cached and displayed to customers. With the previous version of ASP.NET, the solution to this problem would require either manually removing the page from cache using Response.RemoveOutputCacheItem or waiting until the duration time expires and allow the system to update the page automatically.

ASP.NET 2.0 addresses this issue by providing support for database cache dependencies. Table level notifications are available when working with SQL Server 7, 2000, and SQL Server 2005. SQL Server 2005 offers greater granularity on when notifications occur. For example, the following code caches a product page for up to 1 hour, but add a second dependency on a database table:

<%@ outputcache duration="3600" varybyparam="ProdID" sqldependency="MyDatabase:Products" %>

Listing 6. ASP.NET 2.0 Database Cache example

Using the new sqldependency attribute, the cached page will expire if any changes are made to the Products table. The sqldependency attribute must reference a datasource configured in the Web.config file. The datasource identifies the database connection and necessary parameters to make the dependency notification work.

Custom Cache Dependencies

ASP.NET 2.0 ships with one CacheDependency implementation, the SQLCacheDependency class, which supports the various versions of Microsoft SQL Server. Implementing a new cache dependency would be an involved process but is possible due to the extensible nature of ASP.NET 2.0. In other words, you could create your own CacheDependency class to provide similar functionality for other database systems such as Oracle or Sybase.

Post Cache Substitution

For those cases where a few page elements remain dynamic but the majority of a page would benefit from caching, ASP.NET 2.0 provides a feature known as Post Cache Substitution. Post Cache substitution is used to inform the ASP.NET runtime that a particular element, while present on a cached page, should be reevaluated before the page is presented to the user.

There are two ways to use this feature:

  • Call the new Response.writeSubstitution method passing a reference to the substitution callback function.
  • Add an <asp:substitution> control to the Web page and set the methodname attribute to the name of the callback function.

For either option, you will need to add an @OutputCache directive to the page in order to specify the duration and location of the dependency.

Implementing Post-Cache Substitution

Controls that are post-cache-substitution-aware can be created to take advantage of this feature. An example of such a control is the AdRotator control. Listing 7 illustrates a page that:

  • Retrieves data from the authors table of the Pubs database
  • Binds the data to a GridView control
  • Displays ads from an AdRotator
  • Displays the time the page was created in a label control.

An <asp:substitution> control (bold lines in the listing) has also been added to the example. This control has its methodname attribute set to uncachedUpdate (a method that returns string output – in this case, the current time). The substitution control will return the correct time, regardless of what has been cached.

<%@ Page language="c#" Codefile="PostCache.ASPX.cs" 
  AutoEventWireup="true" Inherits="WebApplication1.PostCache" %>
<%@ outputcache duration="30" varybyparam="none" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<html>
  <head>
    <title>WebForm1</title>
  </head>
  <body>
    <form id="Form1" method="post" runat="server">
      <div style="DISPLAY: inline; Z-INDEX: 101; 
        LEFT: 28px; WIDTH: 160px; POSITION: relative; TOP: 33px; 
        HEIGHT: 8px; text-align: right;">
          this page was created at:
      </div>
      <div style="DISPLAY: inline; Z-INDEX: 101; 
        LEFT: 36px; WIDTH: 211px; POSITION: relative; TOP: 62px; 
        HEIGHT: 8px; text-align: left;">
      <asp:substitution id="UpdatedTime" methodname="uncachedUpdate"
         runat="server">
      </asp:substitution>
          </div>
      <asp:Label id="CreatedTime"
        style="Z-INDEX: 102; LEFT: 208px; POSITION: absolute;
        TOP: 47px" runat="server" Width="120px" Height="16px">
      </asp:Label>
      <div style="DISPLAY: inline; Z-INDEX: 101; LEFT: -355px; 
        WIDTH: 160px; POSITION: relative; TOP: 61px; HEIGHT: 8px; text-align: right;">
          and last updated at:
      </div>
      <asp:AdRotator id="Ads" style="Z-INDEX: 105; LEFT: 344px;
        POSITION: absolute; TOP: 42px" runat="server"
        Width="80px" Height="60px" AdvertisementFile="~/img/Ads.xml">
      </asp:AdRotator>
    </form>
  </body>
</html>

Listing 7. PostCache.ASPX Source code

The code behind file for this page contains the event necessary to support the post-cache substitution on the uncachedUpdate method. Note that the Page_Load method reports the time the page was loaded so that we can determine when caching is occurring.

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 WebApplication1
{
  public partial class PostCache : System.Web.UI.Page
  {
    private void InitializeComponent()
    {
      this.Load += new System.EventHandler(this.Page_Load);
    }

    private void Page_Load(object sender, System.EventArgs e)
    {
      CreatedTime.Text = DateTime.Now.ToShortTimeString();
    }

    public static string uncachedUpdate(HttpContext context)
    {
      return DateTime.Now.ToShortTimeString();
    }

  }
}

Listing 8. PostCache.ASPX.cs

What's actually happening is that the substitution control effectively ignores the page directive indicating that the page should be cached for 30 seconds. The substitution control will always call into the code behind file and execute the uncachedUpdate() method.

Post-Cache substitution in action

Figure 6 shows the output from the PostCache page. The first time the application is run we can see that the page created and last updated times are the same.

ms379581.internals_fig06(en-US,VS.80).gif

Figure 6. Output from PostCache.ASPX

On subsequent calls to the same page we can see the effect of post cache substitution. Although the page creation time and the image remain the same, the Last updated time changes.

ms379581.internals_fig07(en-US,VS.80).gif

Figure 7. PostCache Output on second request

Both the creation time and adRotator image remain constant because of the caching directive. The page is cached for 30 seconds. Once the time has passed, both the creation time and the adRotator would update on the next request. However, the <asp:substitution> control, which calls the uncachedUpdate() method, will update every time the page is requested regardless of its cached status.

With proper manipulation of post cache substitution, developers can dramatically increase the performance of their Web applications by updating only the dynamic aspects of their pages. Coupled with database cache invalidation and asynchronous page updates, Web applications developed with ASP.NET 2.0 will remove many of the limitations imposed by the Web's traditional request and response architecture.

Performance

With the infrastructure changes and additional features in ASP.NET 2.0, one question that remains is how fast ASP.NET 2.0 performs? Although performance metrics are always a difficult subject to discuss, significant effort was made to ensure that performance remained consistent or improved throughout all aspects of the ASP.NET 2.0 framework. Evaluating performance is especially difficult when comparing ASP.NET 1.x to ASP.NET 2.0 because of the new architecture and new feature sets.

Improved Request Pipeline

One area in which every developer will see improved performance is in the request pipeline. Despite the addition of many new event hooks, the basic ASP.NET request stack can be up to 30% faster than in ASP.NET 1.1. You can evaluate the improved performance by creating a simple page that displays "Hello World." Because the page has no advanced functionality, you are directly testing the HttpHandler and HttpModule pipeline, as well as the ISAPI plug-in that connects ASP.NET 2.0 to IIS. Regardless of which version of IIS you are using, you should see increased performance as this code has been optimized for faster throughput.

Resource Management Improved with IIS 6.0

Some of the performance improvements in ASP.NET 2.0 only come in conjunction with IIS 6.0. For example, on IIS 6.0, the working set for the worker process was reduced by sharing core framework assemblies across Application Domains. Although this change won't be obvious to most ordinary users, it offers dramatic improvements for multiple application hosting scenarios.

In another test designed to mimic a moderately complex ASP.NET page, the system load (memory and CPU usage) dropped dramatically compared to the same page running on IIS 5.0. This particular performance enhancement was accomplished by moving the response buffers from managed memory to native memory. By removing the necessity to pin managed memory to a particular response, ASP.NET 2.0 eliminates a resource bottleneck and generated faster responses for each request.

Other performance improvements take advantage of IIS 6.0's close integration with the Windows Operating System kernel. IIS 6.0 performs some of its caching and buffering at the kernel level, which provides increased performance for all Web applications, including ASP.NET.

Support for 64 Bit Architectures

One of the major limitations in high end ASP.NET 1.x applications was that the available memory space was limited to 32 bit addressing, regardless of the underlying architecture. Memory intensive (or high volume) Web applications would often throw OutOfMemoryException errors as soon as the 2GB of user-mode virtual address space was consumed. These errors were often random and hard to reproduce, especially since the garbage collection mechanism used the same address space during cleanup operations. Extending the user-mode address space (at the expense of kernel-mode memory) had its own problems, and was still limited by the max 32 bit pointer size of 4 GB.

In ASP.NET 2.0, the .NET framework takes full advantage of 64 bit architectures. The user-mode virtual address space on x64 is 8TB (yes, terabytes). Furthermore, the non-paged pool size is 128GB on x64, whereas it was only 256MB on x86 (32 bit platforms). Both of these improvements effectively eliminate the memory bottleneck found in ASP.NET 1.x applications, provided you move to x64 hardware.

If you are not ready to fully move to x64 mode for all parts of your application, you can still gain some performance advantages by running IIS6 in 32 bit mode on x64 hardware. The 32 bit mode still allows your ASP.NET applications to take advantage of up to 3GB of the user-mode address space. Also, x64 hardware is faster for ASP.NET than x86 hardware, even in 32-bit mode.

Summary

ASP.NET 2.0 contains many architectural enhancements designed to improve developer productivity. Not only has the code model been improved to reduce conflicts, the compilation process has been expanded to provide a wider variety of options for compiling and deploying Web applications. The extensibility of the ASP.NET framework has once again been shown through the new HttpModules and HttpHandlers which support many of the new features found in ASP.NET including personalization, master pages and the administrative site. Caching has been improved to allow for database dependencies and post cache substitution. Internally ASP.NET 2.0 contains drastic improvements over its predecessor; the new implementation incorporates a number of developer driven improvements as well as adhering to industry best practices. ASP.NET 2.0 provides a world class Web development platform that is built to handle the complexity of enterprise Web application development.

 

About the authors

Jayesh Patel—Jay Patel is a developer in both .NET and Java Technologies. Jay's research focuses on pattern-based programming and agile methodologies.

Bryan Acker—Bryan Acker is a technical writer for Infusion Development. Bryan has a strong background in ASP and ASP.NET Web development and Web hosting.

Robert McGovern—Rob McGovern is a senior writer, developer, and project manager for Infusion Development. Rob has worked on several different ASP.NET projects, including CodeNotes for ASP.NET and the JSP to ASP.NET migration guide. Rob is also a Microsoft MVP for Virtual Earth.

Infusion Development Corporation is a Microsoft-Certified Solutions Provider offering customized software-development, training, and consulting services for Fortune 1000 Corporations, with an emphasis on the financial-services industry. With offices in New York and Toronto, Infusion Development has established an international client base, including some of the world's largest companies in the financial service, securities brokerage, and software development industries.

© Microsoft Corporation. All rights reserved.