作者:纤沙湖之歌 | 来源:互联网 | 2024-12-15 13:46
从ASP.NET 1.1过渡到2.0,编译系统的演进带来了显著的变化。在撰写《深入理解ASP.NET组件设计》一书时,我们对ASP.NET 1.1的即时编译模型进行了深入分析,这一部分以一张详细的图表开始,逐步展示了背后的架构设计。随着ASP.NET 2.0的到来,即时编译模型经历了重大调整,新的模型更加复杂和强大。
在1.1版本中,当用户请求一个文件时,ISAPIRuntime(IIS请求处理对象)根据文件类型调用相应的Http Handler,对于.aspx文件,调用的是PageHandlerFactory,它是即时编译系统的入口点。PageHandlerFactory使用PageParser解析.aspx文件,并通过PageCompiler生成编译后的文件。而在2.0版本中,这一过程由BuildManager接管,它负责调用合适的BuildProvider来处理请求的文件,最终由适当的Compiler完成编译。
BuildManager的一个重要特性是能够根据不同的文件扩展名使用不同的BuildProvider,这意味着开发者可以编写自定义的BuildProvider参与即时编译流程。例如,在Visual Web Developer的“新建项”选项中,可以看到ASP.NET 2.0允许用户创建Generic Handler,即1.1版本中的自定义Http Handler程序文件。这个向导会生成如下代码:
```csharp
<%@ WebHandler Language="C#" Class="Handler" %>
using System.Web;
public class Handler : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.COntentType= "text/plain";
context.Response.Write("Hello World");
}
public bool IsReusable {
get {
return false;
}
}
}
```
值得注意的是,过去编写这类自定义Http Handler时,开发者必须预先编译代码并放置在网站目录下,才能确保Handler正常工作。但在2.0版本中,这一过程由BuildManager自动完成,它会查找.ashx文件对应的BuildProvider(即WebHandlerBuildProvider),并运行即时编译模型。
此外,BuildManager还支持预编译模型,使得开发者可以在提供脚本文件和BuildProvider后,同时享受即时编译和预编译的优势。例如,如果一个开发者希望提供一种简单的脚本语言供用户使用,只需实现一个自定义的BuildProvider(如MyScriptBuildProvider),并将其与特定的文件扩展名关联,然后利用CodeDom生成实际的代码即可。
### 即时编译系统的重构
在2.0版本中,尽管PageParser仍然用于解析.aspx文件,但这一过程不再由PageHandlerFactory直接调用。图5展示了2.0版本中Web页面的编译流程概览。
![图5](/default/index/img?u=aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vaW1hZ2VzL2NuYmxvZ3NfY29tL2Fuc3dlcmNhcmQvV2luZG93c0xpdmVXcml0ZXIvSW5zaWRlQVNQLk5FVDIuMF8xNDQwQS9JbnNpZGVfQVNQTkVUMjBfMV9pbWdfNF90aHVtYi5qcGc)
当BuildManager接收到编译指令时,首先会编译目录中的外部文件,包括资源文件(Resource)、Web引用(Web Reference)、代码文件(Code)、配置文件(Profile)以及Global.asax文件。接下来,BuildManager会编译网站中的.aspx文件或其他具有相应BuildProvider的文件。
### BuildManager、BuildProvider和CodeGenerator的关系
BuildManager与BuildProvider及其CodeGenerator之间存在紧密的联系。图6展示了2.0版本中的一部分BuildProvider对象,其中包括许多熟悉的对象名称,这些对象对应于ASP.NET中可编写的文件类型。
![图6](/default/index/img?u=aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vaW1hZ2VzL2NuYmxvZ3NfY29tL2Fuc3dlcmNhcmQvV2luZG93c0xpdmVXcml0ZXIvSW5zaWRlQVNQLk5FVDIuMF8xNDQwQS9JbnNpZGVfQVNQTkVUMjBfMV9pbWdfNV90aHVtYi5qcGc)
例如,Page Theme也有一个PageThemeBuilderProvider,这意味着Theme文件(.skin)在编译后成为控制台实体,从而提高了性能。
### 预编译系统
预编译系统实际上是即时编译系统的一种形式。当BuildManager启动时,会检查请求目录中是否存在.compiled文件,如果存在,则进入预编译模式,加载.compiled文件中定义的Assembly。在预编译模式下,BuildManager不会使用BuildProvider,而是直接从缓存中获取编译结果。
### 自定义BuildProvider
ASP.NET 2.0允许开发者编写自定义的BuildProvider。在web.config文件中,可以通过以下配置定义自定义BuildProvider:
```xml
```
下面是一个简单的自定义BuildProvider示例:
```csharp
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Web.Compilation;
namespace TestBuildProvider
{
public class MyCSharpBuilder : BuildProvider
{
public override void GenerateCode(AssemblyBuilder assemblyBuilder)
{
TextReader reader = base.OpenReader();
string scriptString = reader.ReadLine();
CodeCompileUnit unit = new CodeCompileUnit();
unit.Namespaces.Add(new CodeNamespace("TEST"));
CodeTypeDeclaration class1 = new CodeTypeDeclaration("HelloClass");
class1.IsClass = true;
CodeMemberMethod method1 = new CodeMemberMethod();
method1.Name = "SayHello";
method1.ReturnType = new CodeTypeReference("System.String");
method1.Statements.Add(new CodeMethodReturnStatement(
new CodePrimitiveExpression(scriptString)));
method1.Attributes = MemberAttributes.Public;
class1.Members.Add(method1);
unit.Namespaces[0].Types.Add(class1);
assemblyBuilder.AddCodeCompileUnit(this, unit);
}
}
}
```
### 表达式构建器
除了BuildProvider,ASP.NET 2.0还引入了ExpressionBuilder,这是一种简单的动态解析系统。例如,以下代码片段展示了如何使用内建的ExpressionBuilder:
```html
```
要在web.config中定义自定义的ExpressionBuilder,可以使用以下配置:
```xml
```
### 结语
尽管.NET Framework 2.0仍处于Beta阶段,但BuildProvider和ExpressionBuilder技术为开发者提供了丰富的扩展可能性。然而,最终版本是否会保留这些功能尚不确定,因此建议开发者关注官方文档的更新。