网址:
http://www.microsoft.com/china/msdn/library/webservices/asp.net/Internals.mspx
http://msdn.microsoft.com/asp.net/default.aspx?pull=/library/en-us/dnvs05/html/Internals.asp
简介:该文章精彩概述了asp.net1.1是如何进行编译工作,并详细介绍了asp.net2.0的编译和改变。如果你是使用Whidbey(Visual Studio 2005的Preview 2 版)和ASP.NET进行开发的,新的编译模式是我所喜爱的asp.net特性之一,即使你坚持使用保守的code behind模式,Partial classes也使得code behind模式变得大为易用来。(http://www.west-wind.com/weblog/posts/720.aspx)
当然,也有人认为code inline这是一个倒退,有人认为谣言四起大为疑虑,这也许是asp 2.0正式版发布前的阵痛吧。
恩,该文章如下:
ASP.NET 2.0 的内部变化
Jayesh Patel、Bryan Acker 和 Robert McGovern
Infusion Development
适用范围:
Microsoft ASP.NET 2.0
摘要:尽管 ASP.NET 2.0 与 ASP.NET 1.1 完全向后兼容,但还是为 ASP.NET 带来了大量的内部变化,包括代码模型、编译、页面生命周期等的变化。本文将概括介绍这些变化。
本页内容
引言 | |
代码模型 | |
编译 | |
完全运行时编译(代码目录) | |
页面生命周期 | |
可扩展性 | |
高级缓存技术 | |
性能 | |
结论 |
引言
对 于专业的 ASP.NET 开发人员来说,与 ASP.NET 2.0 有关的重要问题是内部发生了哪些变化。尽管新功能很有趣而且学起来很有意思,但对于真正想掌握这一技术的开发人员来说,ASP.NET 核心结构发生的变化才是最吸引他们的地方。在本白皮书中,我们将介绍自版本 1.x 以来,ASP.NET 2.0 内部结构发生了什么样的变化。
本白皮书介绍的主题对那些关注性能的开发人员以及寻求如何优化应用程序的技术设计师非常有用。具体来说,我们将介绍有关代码模型、编译、页面生命周期、可扩展性、性能和缓存的主要问题。
本文中的许多示例要求您对 ASP.NET、Visual Basic .NET 和/或 C# 语法有相当程度的了解。本文还在适当的地方提供了参考文档,就某些特定的主题展开深入的讨论。
代码模型
也许 ASP.NET 2.0 内部工作方式最明显的变化是 ASP.NET Web 页面的创建方式的变化。本节将介绍内含代码模型发生的变化以及这些变化对 ASP.NET 开发的影响。
ASP.NET 1.x 中的代码模型
在 ASP.NET 1.x 中,供开发人员开发 Web 窗体的主要选择有两个。首先,开发人员可以参照传统的 ASP 模型并直接在 ASPX 页面中编写代码。此过程称为“内嵌代码”,它非常适用于简单的命令。然而,对于更复杂的代码而言,编写内嵌代码将为读取混合了表示 (HTML) 和功能(代码)的 Web 页面带来困难。在 ASP.NET 中,为了帮助解决这个问题,已更改了默认的编码方法。您可以在单独的、只包含代码的文件(称为“内含代码”文件)中编写业务逻辑和事件处理代码。内含代码 模型将只包含代码的文件与包含表示标记的 ASPX 文件链接起来。通过将代码与表示相分离,开发小组可以让设计人员处理演示文件,而让开发人员处理代码文件,从而提高开发小组的工作效率。
图1:ASP.NET 1.x 代码模型
内含代码模型面临的主要困难在于如何将内含代码文件与 ASPX 页面保持同步。尽管从编程意义上来讲 ASPX 页面是从内含代码文件继承而来的,但实际上这两个文件是通过更复杂的关系联系在一起的。
有关 ASP.NET 1.x 中的内含代码模型的详细信息,请参见 MSDN Library 文章 Web Forms Code Model(英文)。
继承的复杂性
ASP.NET 的设计模式就是让开发人员使用 Microsoft Visual Studio .NET 将控件拖放到 ASPX 页面中。Visual Studio 然后将在内含代码文件中自动生成适当的支持代码。如果控件已被添加到 ASPX 页面中,则必须在内含代码文件中添加新代码。换句话说,尽管 ASPX 页面继承自内含代码文件,但实际上 ASPX 页面推动了内含代码文件的设计。
编译的复杂性
第 二个同步问题就是如何编译文件。所有的内含代码文件以及所有的支持类都被编译到一个程序集中,并存储在 Web 应用程序的 /bin 目录中。应用程序的编译工作是在部署工作之前进行的。而另一方面,ASPX 页面也是在第一次被请求时在运行时编译的。ASP.NET 运行库实际上将 ASPX 页面编译到该页面自己的临时程序集中。
此过程的问题在于,无需更新内含代码程序集即可更改 ASPX 页面。也就是说,开发人员可以在部署后更改某个属性或更改 ASPX 页面上某个控件的类型,而无需更新内含代码文件,也不用重新编译应用程序的程序集。出现这种情况时,由于内含代码文件与所关联的 ASPX 页面不匹配,应用程序可能会遇到意外的错误。
ASP.NET 2.0 中的代码模型
ASP.NET 2.0 继续提供内嵌代码和内含代码模型。从内嵌代码模型的角度来说,除了 Microsoft Visual Studio 2005 支持单文件开发的方式有所变化外,基本上没做什么改动。有关 Visual Studio 2005 中的变化及其如何处理内嵌代码的详细信息,请参阅这篇文章(英文)。
ASP.NET 2.0 通过改变内含代码文件的本质解决了内含代码模型的继承和编译问题。在 ASP.NET 2.0 中,内含代码文件不再是 System.Web.UI.Page 类的完整实现。相反,内含代码文件是一个新的构造函数,称为“局部类”。局部类包含所有用户定义的代码,但是不包含 Visual Studio .NET 在 ASP.NET 1.x 中自动生成的任何管线和连接代码。请求包含新的内含代码文件的 ASPX 页面时,ASP.NET 2.0 运行库实际上将把 ASPX 页面和局部类合并成一个类,而不是两个单独的类。
图 2:ASP.NET 2.0 中的内含代码模型
局部类使用新的关键字(在 Visual Basic 中为 Expands,在 C# 中为 Partial)指示在运行时将该类中的代码与另一个类中的代码相合并。类似地,ASPX 页面使用新的指令(称为 compilewith)指示该页面与内含代码文件之间的关联。
比较内含代码文件
如 果您熟悉传统的 ASP.NET 1.x 内含代码文件,您应该知道 Visual Studio 将插入自动生成的控件声明和初始化代码。这种自动生成的代码是内含代码文件与 ASPX 文件之间双向同步的直接结果。带有标签的典型 ASPX 页面具有一个对应的内含代码文件,该文件由许多自动生成的文本行构成。
列表 1:ASP.NET 1.x 中的内含代码文件
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
}
}
自动生成的代码不仅定义标签(以粗体显示的行),还声明新的事件(页面加载)并自动将该事件绑定到自动生成的方法包装程序 (Page_Load())。
比较而言,同一个 ASP.NET 页面在 ASP.NET 2.0 中生成的内含代码文件更简洁。
列表 2:ASP.NET 2.0.x 中的内含代码文件
namespace ASP {
public partial class Webform1_aspx
{
}
}
开发人员可以自动访问 Label1,并根据需要添加事件。例如,可以添加 Page_Load 事件以初始化标签。
列表 3:在新的内含代码文件中添加事件
namespace ASP {
public partial class Webform1_aspx
{
void Page_Load(object sender, EventArgs e)
{
Label1.Text = "Hello ASP.NET 2.0";
}
}
}
事件语法可通过 Visual Studio .NET 生成。生成的内含代码文件更短,而且不包含任何自动生成的代码。ASP.NET 运行库会自动将内含代码文件中的事件绑定到 ASPX 页面上的控件。换句话说,ASP.NET 运行库现在可以自动执行代码生成,而这原本是由 Visual Studio 执行的。
继承的复杂性
新 的内含代码模型大大降低了继承的复杂性。由于 ASPX 页面不是直接继承自内含代码文件,因此内含代码文件不再需要定义并支持 ASPX 页面上定义的所有控件。类似地,内含代码文件可以自动访问 ASPX 页面上的任何控件,而无需声明代码(声明代码在 ASP.NET 1.x 中是必需的)。这一切之所以能够实现,是因为 ASP.NET 运行库可以将所需的声明代码和事件绑定代码自动插入到最终编译的文件中。因为这些工作由运行库来完成,所以代码开发人员和 Web 开发人员都无需再担心这个问题。
在设计期间,链接将由 Visual Studio 2005 来维护。Visual Studio 环境将利用 ASP.NET 运行库编译段来确保代码开发人员和 Web 开发人员可以同步工作。
编译的复杂性
由 于新的内含代码文件与 ASPX 页面链接在一起并在运行时被编译到一个完整的类中,因此编译的复杂性不复存在。也就是说,内含代码文件将自动与 ASPX 页面保持同步。即使新的编译模型也可能包含未同步的代码,但由于产生的异常非常清楚,因此可以快速找到问题的根源。
编译
自 从在 ASP.NET 1.x 中引入页面模型后,ASP.NET Web 页面的编译过程一直被分为两个阶段。首先,将内含代码文件和其他支持类编译到一个程序集中,然后在运行时编译各个 ASPX 文件。尽管此模型有很多优点,但它还有几个缺点。ASP.NET 2.0 为基本模型提供了几种替代方案,从而扩大了编译的选择范围,您可以根据特定需要进行选择。
ASP.NET 1.x 中的编译
ASP.NET 1.x 中的主要编译模型生成了一个应用程序程序集(包含所有已编译的内含代码文件和其他源代码)和一个临时程序集(专门为请求的每个 ASPX 页面而创建)。在某些情况下,编译器优化(例如批处理)可能会导致临时 ASPX 页面被编译到同一个程序集中。在任何情况下,都会将每个 ASPX 页面编译到一个临时程序集中,因此该页面可以加载到 ASP.NET 运行库中。
图 3:ASP.NET 1.x 中的编译
尽 管此模型有很多优点,但它还有两个主要的缺点。首先,必须以用户可以阅读的格式将 ASPX 页面部署到 Web 站点上。如果开发人员使用“内嵌代码”模型,那么还需要将某些甚至所有业务逻辑部署到生产服务器上。尽管 IIS 和 ASP.NET 被配置为不显示原始 ASPX 页面,但是狡猾的攻击者仍然可以通过对 Web 服务器开放的任何入口访问这些文件。其次,任何人首次请求 Web 页面时,响应都会比正常情况下慢,因为 ASP.NET 运行库必须编译 ASPX 页面。
在这一过程中,开发人员唯一能够控制的就是是否对 ASPX 页面进行批编译。在 ASP.NET 1.x 中,您可以通过修改 web.config 文件中的 <compilation> 标记来配置批编译。
列表 4:配置批编译
<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>
首次请求 Web 页面时,批编译将通过延长启动时间来缩短加载时间。批编译的另一个优点是将所有的 ASPX 文件编译到一个临时程序集中,而不是将每个页面编译到一个临时程序集中。
ASP.NET 2.0 中的编译
ASP.NET 2.0 为 Web 应用程序提供了四种不同的编译模型:
• | 普 通 (ASP.NET 1.x) - 在普通的 ASP.NET Web 应用程序中,内含代码文件被编译到一个程序集中并存储在 /bin 目录中。Web 页面 (ASPX) 按需进行编译。此模型适用于大多数 Web 站点。但是,编译过程将导致第一次请求 ASP.NET 页面时的响应速度比后续请求慢。ASP.NET 2.0 继续支持这种编译模型。 |
• | 批编译 - 在 ASP.NET 2.0 中,您可以使用一个 URL 请求对任何应用程序进行批编译。就像在 ASP.NET 1.x 中一样,批编译消除了首次请求页面时的延迟,但延长了启动时间。此外,批编译仍然要求在部署之前编译内含代码文件。 |
• | 部 署预编译 - 这是 ASP.NET 2.0 提供的一项新功能,允许在部署之前完整地编译项目。在完整编译中,根据应用程序的大小和编译设置,可以将所有的内含代码文件、ASPX 页面、HTML、图形资源和其他后端代码编译到一个或多个可执行的程序集中。这些程序集包含 Web 站点的所有已编译代码,并原封不动地复制了资源文件和配置文件。这种编译方法提供了最好的性能和安全性,但代价是您无法在部署后修改 Web 站点。如果您正在部署高度可见或高度安全的 Web 站点,那么此选项是最终部署的最佳选择。但是,如果您正在构建在本地 Intranet 上运行的小站点,而且站点更改频繁,那么完整的预编译可能是多余的。 |
• | 完 整的运行时编译 - 与部署预编译相反的另一个极端情况是,ASP.NET 2.0 提供在运行时编译整个应用程序的新机制。也就是说,您可以将未编译的内含代码文件和其他关联的代码放入新的代码目录中,在运行时根据这些文件生成程序集并 让 ASP.NET 2.0 创建并维护对这些程序集的引用。此选项为更改 Web 站点的内容提供了最大的灵活性,但代价是将未编译的代码存储在服务器上。 |
您可以根据实际的情况和需要选择最佳的编译选项,但编译模型仍然是灵活的。即使您选择使用代码目录来存储内含代码文件,也仍然可以使用完整编译这种方法来部署应用程序。
批编译
web.config 批编译设置在 ASP.NET 2.0 中仍然有效。批编译的优点在于,页面可以立即显示给第一个用户,并且可以在批编译过程中检测到 ASPX 页面中的任何错误。但是,批编译确实会延长应用程序的启动时间,而且必须内置在 web.config 文件中。
部署预编译
部署预编译允许您创建一个或多个程序集,作为 Web 站点的可执行版本。生成的程序集包含 Web 站点的已编译代码。HTML 页面、资源、配置文件和 ASPX 页面将被分别复制。
部署预编译需要使用 aspnet_compiler.exe 命令行实用程序,此实用程序将创建目标部署目录,其中包含用于存储程序集的 /bin 目录以及各种 ASPX 页面的 Stub 文件。还可以使用此实用程序执行在位预编译,与调用“魔术页面”的行为类似。Stub 文件使用 ASPX 页面的名称,但包含调用已编译程序集的简单代码。换句话说,ASPX 页面是简单的空壳,而不是功能完善的页面。
通 过在部署之前预编译 Web 站点,安全性将大大提高,因为如果不对程序集进行反编译,将无法访问任何代码。为了增强保护,您可以打乱生成的程序集,使您的 Web 应用程序更安全。部署预编译的主要缺点在于您必须在部署之前完成这些操作,而且不能在部署后更改 Web 站点。如果要进行更改,必须重新编译 Web 站点并重新部署。
对于大多数主要的 Web 应用程序而言,部署预编译选项是实现部署的首选机制,因为它可以减少在 Web 服务器上部署的原始代码数量,并能提供最高的安全性。可以将增加的进程内置在通常的开发/测试/部署周期中,而不会严重降低工作效率。
完全运行时编译(代码目录)
在上述三种编译方法中,您必须在部署之前编译所有的代码文件(内含代码和支持类)。ASP.NET 2.0 中提供了代码目录。
代 码目录是一个特殊的目录,用于存放未编译的类。在运行时,ASP.NET 运行库会将此目录中的内容编译到一个程序集中,该程序集将由应用程序中的 ASPX 页面自动引用。换句话说,使用代码目录,就无需为支持代码创建和引用单独的程序集。代码目录的优点在于,您无需完整地编译项目即可部署,从而降低了不匹配 的可能性。但缺点是,您可能需要将未编译的代码存储在服务器上。
此选项特别适用于不需要大量支持代码(不管是以内含代码文件还是外部对象的格式)的 ASP.NET 应用程序。对于简单的应用程序而言,快速部署和测试系统的能力要比更稳健的编译方法更具优势。
页面生命周期
ASP.NET 2.0 在 ASP.NET 页面的生命周期方面有两个主要的变化。首先,ASP.NET 2.0 提供了新的事件以支持新功能,这些功能包括母版页、个性化和集成的移动设备支持。其次,ASP.NET 2.0 引入了跨页发送 Web 窗体的技术。
新事件
与 ASP.NET 1.x 相比,ASP.NET 2.0 提供了更精确的页面生命周期方法堆栈。这些新增的方法为 Web 开发人员提供了更高级别的控制。可以通过任何 ASP.NET 页面上的“Page”对象访问这些事件。
表 1 显示了全面的方法列表。“方法”列显示了实际的事件方法名称,“活动”列指示事件是始终处于活动状态还是仅在 PostBack 操作期间处于活动状态。例如,可以使用新方法 TestDeviceFilter 来确定哪个设备筛选器可用,并使用此信息决定如何显示页面。换句话说,新方法 LoadControlState 仅在回发期间调用。可以替代此方法(与 SaveControlState 结合使用),以创建用于在回发期间保存和恢复控件状态的替换序列化方案。
表 1:页面生命周期方法 | |
方法 | 活动 |
Constructor | 始终 |
Construct | 始终 |
TestDeviceFilter | 始终 |
AddParsedSubObject | 始终 |
DeterminePostBackMode | 始终 |
OnPreInit | 始终 |
LoadPersonalizationData | 始终 |
InitializeThemes | 始终 |
OnInit | 始终 |
ApplyControlSkin | 始终 |
ApplyPersonalization | 始终 |
OnInitComplete | 始终 |
LoadPageStateFromPersistenceMedium | PostBack |
LoadControlState | PostBack |
LoadViewState | PostBack |
ProcessPostData1 | PostBack |
OnPreLoad | 始终 |
OnLoad | 始终 |
ProcessPostData2 | PostBack |
RaiseChangedEvents | PostBack |
RaisePostBackEvent | PostBack |
OnLoadComplete | 始终 |
OnPreRender | 始终 |
OnPreRenderComplete | 始终 |
SavePersonalizationData | 始终 |
SaveControlState | 始终 |
SaveViewState | 始终 |
SavePageStateToPersistenceMedium | 始终 |
Render | 始终 |
OnUnload | 始终 |
通过查看页面生命周期的低级别详细信息,我们可以发现在何处能够自然地实现 ASP.NET 2.0 中的许多功能,例如主题和个性化。例如,可以在 IntializeThemes 事件中处理一个主题,在 LoadPersonalizationData 中加载个性化数据,并在以后应用于 ApplyPersonalization 方法。请注意,对于决定 Web 应用程序的最终外观的 UI 元素而言,方法的顺序极其重要。
跨页发送
页 面生命周期的其他主要变化包括事件和 Web 窗体的回发。在 ASP.NET 1.x 中,Web 窗体是自动回发给其宿主页面的。也就是说,当用户提交窗体时,窗体数据将始终被提交回包含原始窗体的页面。这种设计可以很容易地存储控件状态,但限制了开 发人员执行更复杂操作的能力。
在 ASP.NET 2.0 中,Web 窗体控件有一个新属性,使开发人员可以决定执行提交操作时将窗体数据发送到何处。大多数情况下都需要使用回发机制,因此该机制仍然是默认设置。但是,如果开发人员希望将数据发送到不同的窗体,现在这是可以实现的。
图 4:回发和跨页发送
例如,您可以创建一个多页面向导,其中包含几个不同的窗体。每个窗体按顺序提交给下一个页面,直到用户到达可以执行最终验证的摘要页面。可以在当前上下文中通过 PreviousPage 对象访问上一个页面上的数据。PreviousPage 对象用于存储上一个页面上经验证、可供在当前页面上使用的数据。正是因为有了这个对象,跨页发送才不会牺牲控件的持久性。如果用户需要按顺序备份一个窗体,可以立即访问这些页面数据,而不必重新输入所有数据。
可扩展性
ASP.NET 最初被设计成一个开放式框架。也就是说,构成 ASP.NET 的许多模块和组件都可以扩展、修改或替换以满足您的特定需要。在 ASP.NET 2.0 中,通过新的 HTTPHandlers 和 HTTPModules(现在是框架的标准组成部分)清楚地说明了框架的可扩展本质。
请求管道
在 ASP.NET 中,请求通过 Internet 服务器应用程序编程接口 (ISAPI) 筛选器从 Web 服务器传出,并传递给实际的 ASP.NET 运行库。
图 5:请求管道
当 IIS 接收到请求时,将根据 IIS 设置将扩展名映射到 ISAPI 筛选器。.aspx、.asmx、.asd 及其他扩展名被映射到 aspnet_isapi.dll,这只是一个用于启动 ASP.NET 运行库的 ISAPI 筛选器。当请求遇到 ASP.NET 运行库后,它将在 HTTPApplication 对象(作为 ASP.NET Web 应用程序的主机)上启动。HTTPApplication 对象:
1. | 读取计算机和应用程序级别的配置文件。 |
2. | 将请求传递给一个或多个 HTTPModule 实例。每个 HTTPModule 提供一项服务,例如会话维护、验证或配置文件维护。这些模块将请求传递回 HTTPApplication。 |
3. | 基于动词和路径将请求传递给 HTTPHandler。动词是指请求中使用的 HTTP 动词(GET、POST、FTP 等),路径是指应用程序中的 URL。根据处理程序的配置方式,请求可能会被作为 ASP.NET 页面(System.Web.UI.Page 是 IHTTPHandler 的实现)进行处理,也可能会触发其他操作,例如对所有 Web 页面进行批编译(precomiplation.asd 将触发 PrecompHandler)。 |
在 ASP.NET 2.0 中,此模型没有任何变化,但添加了几个新的模块和处理程序以提供更多服务。就像在 ASP.NET 1.x 中一样,您可以扩展、替换或重新配置任何模块或处理程序类以提供自定义功能。
新模块
显然,新增的 HTTPModules 用于支持 ASP.NET 2.0 中提供的新服务。具体来说,具有默认模块设置的 ASP.NET 应用程序包括为以下目的而添加的新模块:
• | SessionID - 会话标识机制已从 ASP.NET 1.x 会话模块中拆分开来,目的是为了更好地控制 Cookie、URL 重写以及生成会话 ID 的其他形式。 |
• | 角色管理 - 这是一个新增的模块,用于提供基于角色的服务,以支持新的用户标识机制。此模块有助于将 ASP.NET 应用程序链接到 .NET Framework 中内置的、基于角色的安全性。 |
• | 匿名标识 - 支持匿名用户的新的个性化功能。此模块可以帮助跟踪匿名用户可以访问的功能,以及跟踪在请求之间维护这些功能的方式。 |
• | 配置文件 - 配置文件模块与新的配置文件服务相链接,可以帮助提供用户特定的持久性数据存储。 |
• | 页面计数器- ASP.NET 2.0 中增加的一个新模块,用于链接页面计数器并提高 Web 通信的统计分析。 |
除了这些新模块以外,某些旧模块的行为也发生了变化:例如,输出缓存模块现在支持本白皮书后面介绍的新的缓存技术。
新处理程序
除 了新模块以外,ASP.NET 2.0 还引入了新的处理程序,以支持应用程序配置工具和其他新功能,例如批编译请求。最重要的新处理程序包括用来处理 Web 站点管理请求的“.axd”系列。这些处理程序将启动允许开发人员配置 ASP.NET 用户和其他设置的内部管理工具。这些管理处理程序包括:
• | Web 管理 - WebAdminHandler 是管理 Web 站点的主页。此处理程序为管理 ASP.NET 2.0 Web 应用程序提供了一个起点。 |
• | 跟踪 - ASP.NET 1.x TraceHandler 已得到改进,是 ASP.NET 1.x 中唯一可用的“axd”处理程序。 |
• | Web 资源 - 借助新的管理工具和 WebResourcesHandler,现在可以将 Web 资源配置为后部署。 |
• | 缓存的图像 - CachedImageServiceHandler 支持缓存图形组件。 |
• | 计数器 - SiteCountersHandler 使用页面计数器模块来提供 ASP.NET 2.0 应用程序的访问统计信息。 |
• | 预编译 - 如前文所述,可以使用 PrecompHandler 对 ASP.NET 应用程序中的所有 ASPX 页面进行批编译。 |
• | Web 部件导出 - WebPartExportHandler 支持存储和传输 Web 部件布局。Web 部件是一个新机制,用于个性化门户样式的 Web 应用程序的外观和内容。 |
与以前一样,HTTPForbiddenHandler 被链接到不应返回的任何文件类型。在 ASP.NET 2.0 中,被禁止的文件类型列表已被扩展,现在包括母版页、外观文件及其他新的开发人员组件。
高级缓存技术
提高 Web 应用程序性能的一种方法是在内存中缓存静态内容。返回缓存的内容始终要比返回新渲染的内容快。但是,缺点是缓存的内容可能会过期。ASP.NET 1.x 支持几种缓存,包括:
• | 页面级别 - 每个页面可以作为一个整体进行缓存,或基于用来访问该页面的参数进行缓存。缓存的页面将在指定时间后过期。 |
• | 页面片段 - 如果使用用户控件(.ascx 文件)构建页面,则可以独立于页面内容的其余部分单独缓存用户控件。 |
• | 编程缓存 - 由于有了缓存 API,开发人员还可以缓存对象。缓存 API 具有一个显著的优点,它使开发人员可以为何时应刷新缓存创建不同类型的依赖关系。 |
在 ASP.NET 2.0 中,页面级别的缓存机制已得到扩展,以支持数据库依赖关系。借助数据库缓存依赖关系,可以将缓存的页面绑定到 SQL Server 数据库中的特定表。如果表发生变化,缓存将自动过期。此外,开发人员现在可以使用缓存后替换功能,用刷新的内容替换缓存的部分内容。缓存后替换功能允许应 用程序使用页面级别的缓存,即使页面的部分内容应动态生成。
数据库缓存过期
对大多数数据驱动的 Web 站点来说,缓存是一个复杂的主题,特别是在需要缓存且必须更新数据的情况下。在 ASP.NET 1.x 中,页面可以缓存一段时间,并通过输入参数(查询字符串或 POST 参数)进行组织:
列表 5:ASP.NET 1.x 输出缓存指令
<%@ outputcache duration="3600" varybyparam="ProdID" %>
例如,列表 5 中的代码基于变量 ProdID 在内存中将页面缓存一小时。以上示例中出现的问题是,如果相关业务数据在其他地方被更新,应该怎么办?例如,假设按照产品 ID 缓存一个产品目录页面。如果从管理站点更新了此产品的相关信息(例如库存数量或价格),那么缓存的数据将不正确,显示给客户的数据也不正确。在以前版本的 ASP.NET 中,要解决此问题,需要使用 Response.RemoveOutputCacheItem 手动从缓存中删除该页面,或等到 duration 时间过期后让系统自动更新页面。
ASP.NET 2.0 通过支持数据库缓存依赖关系,从而解决了这一问题。使用 SQL Server 7 和 200 时可以使用表级别的通知,并且 Microsoft SQL Server 2005 将提供更精确级别的通知。例如,以下代码最多可以将产品页面缓存一小时,但添加了对数据库表的第二层依赖关系。
列表 6:ASP.NET 2.0 数据库缓存示例
<%@ outputcache duration="3600"
varybyparam="ProdID"
sqldependency="MyDatabase:Products" %>
使用新的 sqldependency 属性时,只要对“Products”表进行更改,缓存的页面就将过期。sqldependency 属性必须引用在 web.config 文件中配置的 datasource。datasource 用于标识数据库连接以及发出依赖关系通知所必需的参数。
自定义缓存依赖关系
ASP.NET 2.0 附带了一个 CacheDependency 实现,即 SQLCacheDependency 类,它支持 Microsoft SQL Server。实现新的缓存依赖关系是一个复杂的过程,但由于 ASP.NET 2.0 具有可扩展性,因此这一过程是可以实现的。换句话说,您可以创建自己的 CacheDependency 类,以便为其他数据库系统(如 Oracle 或 Sybase)提供类似的功能。
缓存后替换功能
对于几个页面元素保持动态更新,而页面的大部分内容适于缓存的情况,可以利用 ASP.NET 2.0 提供的缓存后替换功能。缓存后替换功能用于通知 ASP.NET 运行库是否应在向用户显示缓存的页面之前重新评估该页面上的某个特定元素。
使用此功能的方法有两种:
• | 调用新的 Response.writeSubstitution 方法,以传递对替换回调函数的引用。 |
• | 将 <asp:substitution> 控件添加到 Web 页中,并将 methodname 属性设置为回调函数的名称。 |
• | 不管采用哪种方法,都需要在页面中添加用来指定依赖关系的持续时间和位置的 @OutputCache 指令。 |
实现缓存后替换功能
可以创建支持缓存后替换功能的控件,以充分利用此功能。这类控件的一个示例就是 AdRotator。列表 7 展示了这样一个页面:
• | 从“Pubs”数据库的“authors”表中检索数据。 |
• | 将数据绑定到 GridView 控件。 |
• | 显示来自 AdRotator 的广告。 |
• | 在标签控件上显示创建页面的时间。 |
该示例中还添加了 <asp:substitution> 控件(列表中以粗体显示的行)。此控件将其 methodname 属性设置为 uncachedUpdate(返回字符串输出的方法,本例中为当前时间)。无论缓存了什么内容,替换控件都将返回正确的时间。
列表 7:PostCache.ASPX 源代码
<%@ Page language="c#" Codebehind="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 MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<DIV style="DISPLAY:inline;
Z-INDEX:101; LEFT:32px; WIDTH:160px;
POSITION:absolute; TOP:24px; HEIGHT:8px"
align="right" ms_positioning="FlowLayout">
this page was created at:
</DIV>
<asp:Label id="CreatedTime"
style="Z-INDEX:102; LEFT:200px; POSITION:absolute;
TOP:24px" runat="server" Width="120px" Height="16px">
</asp:Label>
<asp:substitution id="UpdatedTime" methodname="uncachedUpdate"
style="Z-INDEX:103; LEFT:200px; POSITION:absolute;
TOP:48px" runat="server" Width="112px" Height="11px">
</asp:substitution>
<DIV style="DISPLAY:inline; Z-INDEX:104; LEFT:32px;
WIDTH:160px; POSITION:absolute; TOP:48px;
HEIGHT:16px" align="right" ms_positioning="FlowLayout">
and last updated at:
</DIV>
<asp:AdRotator id="Ads" style="Z-INDEX:105; LEFT:312px;
POSITION:absolute; TOP:16px" runat="server"
Width="80px" Height="60px" AdvertisementFile="img/Ads.xml">
</asp:AdRotator>
</form>
</body>
</HTML>
此页面的内含代码文件包含支持 uncachedUpdate 方法的缓存后替换功能所必需的事件。请注意,Page_Load 方法将报告加载页面的时间,因此我们可以确定缓存发生的时间。
列表 8:PostCache.ASPX.cs
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 class PostCache :System.Web.UI.Page {
protected System.Web.UI.WebControls.Label CreatedTime;
protected System.Web.UI.WebControls.Label UpdatedTime;
protected System.Web.UI.WebControls.AdRotator Ads;
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();
}
protected String uncachedUpdate() {
return DateTime.Now.ToShortTimeString();
}
}
}
当前使用的缓存后替换功能
图 6 显示了 PostCache 页面的输出。首次运行该应用程序时,我们可以看到“page created”和“last updated”时间是相同的。
图 6:PostCache.ASPX 的输出
以后调用同一页面时,我们将看到使用缓存后替换功能的效果。尽管创建页面的时间和图像仍然相同,但上次更新时间是不同的。
图 7:第二次请求时的 PostCache 输出
由于使用了缓存指令,因此创建时间和 adRotator 图像仍然保持不变。该页面将缓存 30 秒。30 秒过后,创建时间和 adRotator 都将在下次请求时更新。但是,<asp:substitution> 控件(调用 uncachedUpdate() 方法)将在每次请求页面时更新,而不管其缓存状态。
通 过正确使用缓存后替换功能,开发人员只需更新页面上的动态内容,从而可以显著提高其 Web 应用程序的性能。将使用 ASP.NET 2.0 开发的 Web 应用程序与数据库缓存过期和异步页面更新相集成,可以消除传统的 Web 请求和响应体系结构暴露出来的许多局限性。
性能
尽 管 ASP.NET 2.0 中的基础结构发生了变化而且增加了一些功能,但仍然存在一个问题,那就是 ASP.NET 2.0 的执行速度有多快?因为 ASP.NET 2.0 仍在开发过程中,所以性能方面的数据还无法获得。尽管如此,我们已经付出了很大的努力,以确保 ASP.NET 2.0 框架能够在各方面保持现有的性能或有所改进。
改进的请求管道
所有开发人员都能看到请求管道的性 能有所改进。尽管添加了许多新的事件连接,但基本的 ASP.NET 请求堆栈速度要比在 ASP.NET 1.1 中快出高达 30%。您可以通过创建显示“Hello World”的简单页面来感受一下改进的性能。因为该页面不包含高级功能,所以您可以直接测试 HTTPHandler 和 HTTPModule 管道以及连接 ASP.NET 2.0 和 IIS 的 ISAPI 插件。不管使用哪个版本的 IIS,您都能看到性能方面的改进,因为此代码已被优化以加快处理速度。
使用 IIS 6.0 改进的内存管理
ASP.NET 2.0 中的某些性能改进完全是因为使用了 IIS 6.0。例如,如果使用 IIS 6.0,在使用 100 个并发用户请求一个包含几个控件的页面的加载测试中,辅助进程的工作集减少了大约 50%。这意味着,对于给定服务器,操作系统使用的资源大约是以前必须使用的资源的一半。
在专门模拟中等复杂程度的 ASP.NET 页面的测试中,与在 IIS 5.0 上运行的同一页面相比,系统负载(内存和 CPU 使用量)已显著降低。这种特定的性能改进是通过将响应缓冲区从托管内存移至本机内存来完成的。由于不再需要将托管内存固定到特定的响应,因此 ASP.NET 2.0 不仅消除了资源瓶颈,还能够更快地响应每个请求。
其他的性能改进是通过紧密集成 IIS 6.0 与 Windows 操作系统内核而实现的。IIS 6.0 在内核级别执行某些缓存和缓冲操作,从而提高了所有 Web 应用程序(包括 ASP.NET)的性能。
其他改进
作为一名开发人员,您肯定希望 ASP.NET 2.0 与 ASP.NET 1.x 的操作速度相同或更快。现在,核心功能已经具备,您一定能够从最终版本的 ASP.NET 2.0 中看到所期待的其他性能改进。
结论
为了提高开发人员的工作效率,ASP.NET 2.0 进行了许多体系结构方面的改进。不仅改进了代码模型以减少冲突,还扩展了编译进程,从而为编译和部署 Web 应用程序提供了多种选择。ASP.NET 框架的可扩展性再次通过新的 HTTPModules 和 HTTPHandlers(支 持 ASP.NET 中的许多新功能,包括个性化、母版页和管理站点)显示出来。缓存功能得到改进,允许使用数据库依赖关系和缓存后替换功能。从内部来看,ASP.NET 2.0 与以前的版本相比具有很大的改进;新的实现并入了开发人员驱动的许多改进功能,同时沿用了行业最佳做法。ASP.NET 2.0 提供了世界一流的 Web 开发平台,可以处理复杂的企业 Web 应用程序开发。
参考资料
• | |
• | |
• |
关于作者
Jayesh Patel - Jay Patel 是擅长 .NET 和 Java 技术的开发人员。Jay 主要研究基于模式的编程和灵活的方法。
Bryan Acker - Bryan Acker 是 Infusion Development 的技术撰稿人。Bryan 在 ASP 和 ASP.NET Web 开发及 Web 托管技术方面有着很强的专业背景。
Robert McGovern - Rob McGovern 是 Infusion Development 的高级撰稿人、开发人员兼项目经理。Rob 曾参与几个不同的 ASP.NET 项目,包括“CodeNotes for ASP.NET”和“JSP to ASP.NET migration guide”。
Infusion Development Corporation 是通过 Microsoft 认证的解决方案提供商,为世界 1000 强企业提供量身定做的软件开发、培训和咨询服务,主要服务于金融服务行业。Infusion Development 在纽约和多伦多设有办事处,已建立一定的国际客户基础,包括金融服务、证券经纪和软件开发行业中的一些世界最大的公司。Infusion Development 的员工也是 CodeNotes 系列丛书的作者和创始人。
© 2004 Microsoft Corporation 版权所有。保留所有权利。使用规定。
ASP.NET 2.0 Internals
Jayesh Patel, Bryan Acker, Robert McGovern
Infusion Development
August 2004
Revised March 2005
Applies to:
Microsoft ASP.NET 2.0 (updated for Beta 2 changes)
Summary: While 100 percent backward compatible with ASP.NET 1.1, ASP.NET 2.0 brings a number of internal changes to ASP.NET. These include changes to the code model, compilation, page lifecycle, and more. This article outlines those changes. (21 printed pages)
Contents
Introduction
Code Model
Compilation
Full Runtime Compilation (The Code Directory)
Page Lifecycle
Extensibility
Advanced Caching Techniques
Performance
Conclusion
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, performance, and caching.
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 the internal workings of ASP.NET 2.0 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.
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 Microsoft 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.
The problem with this process is that the ASPX page can be changed without updating the code-behind assembly. That is, a developer may 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 Microsoft Visual Studio 2005 supports single-file development. For more details on changes in Visual Studio 2005 and how it handles code-inline, see this article.
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.
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.
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.
Listing 1. A code-behind file in ASP.NET 1.x
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
}
}
The auto-generated code not only defines the label (bold line), it also declares a new event (page load) and automatically wires the event to an auto-generated method wrapper (Page_Load()).
In comparison, the same ASP.NET page in ASP.NET 2.0 generates a code-behind file that is much cleaner.
Listing 2. A code-behind file in ASP.NET 2.0
namespace ASP {
public partial class MyPage : System.Web.UI.Page
{
}
}
The developer can access Label1 automatically and add events as needed. For example, a Page_Load event can be added to initialize the label.
Listing 3. Adding events in the new code-behind file
namespace ASP {
public partial class Webform1_aspx : System.Web.UI.Page
{
void Page_Load(object sender, EventArgs e)
{
Label1.Text = "Hello ASP.NET 2.0";
}
}
}
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.
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 Web site 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.
Listing 4. Configuring batch-compilation
<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>
Batch-compilation makes a trade, of 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 three different compilation models for a Web application:
- 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 Web sites. 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.
- 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 the compiled code for the Web site 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 Web site post-deployment. If you are working with highly visible or highly secure Web sites, 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.
- The ASP.NET 2.0 compilation model also allows you to precompile all the code-behind files for 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 precompiled class (the page's base class). If you chose to edit your .aspx file at runtime, you must simply recompile your page.
- Full runtime compilation— At the other extreme of deployment precompilation, ASP.NET 2.0 provides a new mechanism to compile the entire application at runtime. That is, you can put your uncompiled code-behind files and any other associated code in the new \app_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 Web site content at the cost of storing uncompiled code on the server.
Choosing the best compilation option will depend on your exact circumstances and needs, but the compilation model remains flexible. Even if you choose to make use of the \app_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 that 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 Web site. The resulting assemblies contain the compiled code for the Web site. 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 precompilation, similar to the 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 Web site 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 you can't change your Web site after it has been deployed. If you want to make changes, you have to recompile the Web site 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 code directory.
The \app_code directory is a special directory that holds uncompiled 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. In other words, by using the code directory, you can avoid creating and referencing separate assemblies for your supporting code. 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 uncompiled 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, and the Active column indicates whether the event is always active or only active during PostBack actions. 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.
Table 1. Page Lifecycle methods
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 |
Looking at the low level details of the page lifecycle, we can see where many of the features that are 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.
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 by means of 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.
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:
- Reads the machine and application level configuration files.
- 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.
- 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, and so on) 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 the batch-compilation of all Web pages (precomiplation.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—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 Web site 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 Web site. 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.
- Counters—The SiteCountersHandler works with the page counters module to provide access statistics for an ASP.NET 2.0 application.
- 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. When the table changes, the 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 Web sites, caching can be a thorny topic, especially when a situation demands the need for caching and the necessity of 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):
Listing 5. ASP.NET 1.x output cache directive
<%@ outputcache duration="3600" varybyparam="ProdID" %>
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 (quantity available or price, for example) 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 allowing 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 and 2000, and Microsoft SQL Server 2005 will offer notifications on an even more granular level. For example, the following code caches a product page for up to 1 hour, but adds a second dependency on a database table.
Listing 6. ASP.NET 2.0 database cache example
<%@ outputcache duration="3600"
varybyparam="ProdID"
sqldependency="MyDatabase:Products" %>
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 that is 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 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 an @OutputCache directive, specifying the duration and location of the dependency, should be added to the page.
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.
Listing 7. PostCache.ASPX Source code
<%@ 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 MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<DIV style="DISPLAY: inline;
Z-INDEX: 101; LEFT: 32px; WIDTH: 160px;
POSITION: absolute; TOP: 24px; HEIGHT: 8px"
align="right" ms_positioning="FlowLayout">
this page was created at:
</DIV>
<asp:Label id="CreatedTime"
style="Z-INDEX: 102; LEFT: 200px; POSITION: absolute;
TOP: 24px" runat="server" Width="120px" Height="16px">
</asp:Label>
<asp:substitution id="UpdatedTime" methodname="uncachedUpdate"
style="Z-INDEX: 103; LEFT: 200px; POSITION: absolute;
TOP: 48px" runat="server" Width="112px" Height="11px">
</asp:substitution>
<DIV style="DISPLAY: inline; Z-INDEX: 104; LEFT: 32px;
WIDTH: 160px; POSITION: absolute; TOP: 48px;
HEIGHT: 16px" align="right" ms_positioning="FlowLayout">
and last updated at:
</DIV>
<asp:AdRotator id="Ads" style="Z-INDEX: 105; LEFT: 312px;
POSITION: absolute; TOP: 16px" runat="server"
Width="80px" Height="60px" AdvertisementFile="img/Ads.xml">
</asp:AdRotator>
</form>
</body>
</HTML>
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.
Listing 8. PostCache.ASPX.cs
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 class PostCache : System.Web.UI.Page {
protected System.Web.UI.WebControls.Label CreatedTime;
protected System.Web.UI.WebControls.Label UpdatedTime;
protected System.Web.UI.WebControls.AdRotator Ads;
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();
}
protected String uncachedUpdate() {
return DateTime.Now.ToShortTimeString();
}
}
}
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.
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.
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 that are 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 does ASP.NET 2.0 perform? Although performance metrics are not available as ASP.NET 2.0 is still in development, significant effort was made to ensure that performance remained consistent or improved throughout all aspects of the ASP.NET 2.0 framework.
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 is 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.
Memory 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 approximately 50 percent in load tests using 100 concurrent users requesting a page with several controls. That means that, for a given server, the operating system is using about half the resources that were necessary before.
In a test designed to mimic a moderately complex ASP.NET page, the system load (memory and CPU usage) dropped dramatically when 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 generates faster responses for each request.
Other performance improvements take advantage of close integration of the IIS 6.0 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.
Other Improvements
As a developer, you should expect ASP.NET 2.0 to perform as fast as, or faster than, ASP.NET 1.x. Now that the core functionality has been built, additional performance improvements should be expected in the final release of ASP.NET 2.0.
Conclusion
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 while 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.
Related Books
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.
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. The employees of Infusion Development are also the authors and creators of the CodeNotes book series.