® .NET Framework 版本中可能会发生变化。)


Temporary ASP.NET Files 文件夹中保存的是什么内容?

ASP.NET 页面请求的处理过程需要使用一些临时文件。当您在 Web 服务器上安装 ASP.NET 2.0 时,所创建的文件夹层次结构如下:

%WINDOWS%\Microsoft.NET\Framework\v2.0.50727

 

图 1)。

领先技术-ASP.NET 页面的服务器端
图 1 ASP.NET 1.0、1.1、2.0 和 3.0 运行库文件 (单击该图像获得较小视图)
领先技术-ASP.NET 页面的服务器端
图 1 ASP.NET 1.0、1.1、2.0 和 3.0 运行库文件 (单击该图像获得较大视图)

® 2005 内部运行该工具。最后,Temporary ASP.NET Files 文件夹包含为页面和资源提供服务而创建的所有临时文件和程序集。要找到为您的 Web 页面动态创建的文件,您需要查看此文件夹子树。请注意,Temporary ASP.NET Files 目录是存放动态创建的文件的默认位置,但可以使用 web.config 文件中的 <compilation> 部分按应用程序对其进行配置:

<compilation tempDirectory="d:\MyTempFiles" />

 

图 2)。

领先技术-ASP.NET 页面的服务器端
图 2 Web 测试服务器上的 WebApp (单击该图像获得较小视图)
领先技术-ASP.NET 页面的服务器端
图 2 Web 测试服务器上的 WebApp (单击该图像获得较大视图)

在应用程序的编译子文件夹下,有一组使用散列名称的目录。此处显示了通常可以找到临时文件的路径。(最后两个目录包含的是假名称,但实际显示的就是这样的名称。)

\v2.0.50727\Temporary ASP.NET Files\MyWebApp\3678b103\e60405c7

 

您可以使用以下语句,以编程的方式检索指定应用程序的临时文件所在的子文件夹的路径:

Dim tempFilesFolder As String = HttpRuntime.CodegenDir

 

ASP.NET 会定期在应用程序发生改变、需要重新编译时清理编译文件夹并删除陈旧的资源,但 Temporary ASP.NET Files 目录下的子树的大小可能会显著地增加,在测试计算机上更是如此。 作为管理员,您应密切关注 Temporary ASP.NET Files 下的目录,并确保所有目录都是与当前活动的应用程序有关的。如果您无意间删除了一个处于活动状态的应用程序的子树,不必惊慌。您将丢失所有预编译的页面和资源并会将应用程序重置到其最初的编译状态;但下一个请求将触发对每个页面或一批页面(具体取决于配置)执行新的编译过程,因此最终不会丢失任何信息或页面,只不过用户在处理下一个请求时将感觉到首次命中延迟。现在,我们来看某一应用程序的编译文件夹的内容。


保留文件

对于应用程序中的每个页面,页面编译进程会生成一个下述名称的文件:

[page].aspx.[folder-hash].compiled

 

[page] 占位符代表 .aspx 资源的名称。[folder-hash] 占位符是一个散列值,它使文件名保持唯一,避免与原本属于其他文件夹的同名文件混淆。这种文件称为保留文件,因为它们包含有重要的信息,这些信息可帮助 ASP.NET 运行库快速检索程序集以及检索将用于为页面请求提供服务的 HTTP 处理程序的类型名称。此外,保留文件还包含一个文件散列值,用于检测自从上次访问后文件的内容是否发生了改变。

构成某一应用程序的所有 .aspx 页面在同一个临时文件夹中进行编译,即使它们名称相同且位于不同的文件夹中也是如此处理。这一点如何实现?假设您的应用程序包含两个名为 test.aspx 的页面,位于不同的文件夹 - Folder1 和 Folder2 中。两个页面将在同一临时文件夹中进行编译,但可以通过它们的散列值对其进行区分,由于散列值是根据路径信息而不只是文件名计算出来的,因此它们的散列值是不同的。因而最终,两个 test.aspx 页面的保留文件名只在文件夹散列值部分有所不同:

Test.aspx.cdcab7d2.compiled
Test.aspx.9d86a5d7.compiled

 

散列值的内部存储缓存使 ASP.NET 运行库可以识别任何指定页面 URL 的散列值并快速找到相应的保留文件。如果没有找到保留文件,ASP.NET 会动态编译页面。当您部署没有预编译的应用程序时就会发生这种情况。另一方面,当您对一个站点进行预编译时,每个组成页面的保留文件被创建并放置在 Bin 文件夹中。

图 3 显示了一个示例保留文件的内容。

图 4 具体列出了文件的属性。<fileDeps> 部分列出了当前页面所依赖的文件。对任何依存关系所做的任何改动都将导致页面重新编译。FileHash 值代表依存关系状态的快照,而 Hash 代表当前页面文件状态的快照。值得注意的是,当您停止或重新启动 Web 应用程序时,完全基于文件更改通知来检测文件动态更改的机制会失败。按照散列值保存页面和依存关系的状态,使您可以随时检测到更改。

类型 (Type) 属性设置动态创建的类(将用于为请求提供服务)的名称。默认情况下,类型名称是 ASP.[page]_aspx,其中 [page] 代表页面文件的名称。但是请注意,您可以通过设置您的 .aspx 文件的 @Page 指令中的 ClassName 属性来更改此名称。根命名空间不会更改,因此类型名称可以是 ASP.[ClassName]。

程序集 (Assembly) 属性指示动态创建的程序集的名称,该程序集包含用于为请求提供服务的页面类。此类程序集的名称和内容取决于 web.config 文件的 <compilation> 部分中的设置。

Extreme ASP.NET 专栏),那么您不必担心首次命中延迟,但您仍应考虑最佳的批处理参数,以避免 Web 服务器的内存过载。

当批处理开启时,应用程序中的前 1000 个页面(实际数量取决于 maxBatchSize)被编译为名为 App_Web_[random] 的程序集,其中 [random] 是由八个字符组成的随机序列。 如果关闭批处理,则每个页面将产生各自的程序集。程序集的名称如下:

App_Web_[page].aspx.[folder-hash].[random].dll

 

要关闭批处理,可向 web.config 文件添加以下内容:

 
<compilation batch="false" />

 

如果您对一个示例应用程序的编译文件夹进行查看,您会找到名称中包含 CBMResult 的附带保留文件,还有一个具有相同名称的 .ccu 文件,如下所示:

test.aspx.cdcab7d2.compiled
test.aspx.cdcab7d2_CBMResult.ccu
test.aspx.cdcab7d2_CBMResult.compiled

 

列表中的第一个文件是保留文件。那么其他两个作何用途? CCU 代表代码编译单元 (Code Compile Unit),是指用于生成动态页面类的源代码而创建的 CodeDOM 树。 CCU 文件是二进制文件,包含经序列化的页面 CodeDOM 树。CBMResult 文件是保留文件,用于检查 CCU 是否最新、其所在的位置以及它基于哪些文件。

®)来使用。这些模块查询页面的结构来获取语句结束信息。CCU 文件会保留准备为这些请求提供服务的页面的最新 CodeDOM 结构副本。


页面类动态源代码

正如上面提到的,.aspx 资源被解析为 Visual Basic 或 C# 类。该类继承自 System.Web.UI.Page,或者很可能继承自某个从 System.Web.UI.Page 继承而来的类。事实上,在大多数常见情形下,动态页面类具有以下原型:

Namespace ASP
    
Public Class test_aspx 
        
Inherits Test : Implements System.Web.IHttpHandler
        领先技术-ASP.NET 页面的服务器端
    
End Class
End Namespace

 

图 5 显示了参与为某一 .aspx 资源的请求提供服务的类集。

图 5 中的类跨两个不同的源文件。第一个包含分部类,用于完善代码文件中的类和由此派生出的用于为请求提供服务的实际页面类。第二个文件是您在项目中创建的代码文件的副本。这些文件根据程序集名称而命名。名称的结构如下:[assembly].X.vb。(如果您使用 C#,则为 .cs)X 为从 0 开始的递增索引值,可确保文件名唯一。

如果您查看示例 test.aspx 页面的编译文件夹的内容,您会发现创建了第三个文件,如下例中所示:

Namespace __ASP
   
Friend Class FastObjectFactory_app_web_test_aspx_cdcab7d2_xg83msu0
      
Private Sub New()
          
MyBase.New
      
End Sub
        
      
Shared Function Create_ASP_test_aspx() As Object
          
Return New ASP.test_aspx
      
End Function
  
End Class
End Namespace 

 

类名称是以字符串 FastObjectFactory 为前缀的页面程序集的名称。该类具有一个名为 Create_XXX 的共享函数(如果以 C# 编写则为静态函数),其中的 XXX 是要实例化的页面类的名称。顾名思义,这是一个帮助器类,ASP.NET 运行库利用其来加速页面实例的创建 - 这是一个非常常见的操作。与编译一个页面相比,创建这种类所花费的时间非常短。另一方面,使用工厂类比使用 Activator.CreateInstance 间接创建对象要快得多。

根据批处理编译设置,工厂类的内容会有所变化。在默认情况下,当批处理开启时,工厂类包含与批处理页面相同数量的 Create_XXX 函数。工厂类的名称与批处理程序集的名称相同:

' Used to serve test.aspx
Shared Function Create_ASP_test_aspx() As Object
   
Return New ASP.test_aspx
End Function

' Used to serve default.aspx
Shared Function Create_ASP_default_aspx() As Object
   
Return New ASP.default_aspx
End Function

 

如果批处理关闭,则工厂类与单个页面程序集的名称相同,并且只包含一个共享函数 - 具体页面的 facotry。在这种情况下,应用程序中的每个页面将有自己的工厂类。


运行库公共 API

借助上面讨论的信息,探究编译文件夹的内容就不是非常困难了。但通过一个工具来帮助您快速找到您所需的信息还是非常方便。 我待会儿将设计一个用来导航动态生成的 ASP.NET 应用程序源代码的资源管理器工具,但首先我们来看一看 .NET Framework 2.0 中的一些运行库 API。特别是,以下两个类可能是您更希望了解的:HttpRuntime 和 ClientBuildManager。

HttpRuntime 类具有大量共享属性,可返回关于包括当前应用程序的 Bin 文件夹、ASP.NET 安装路径、编译文件夹和当前 AppDomain ID 在内的各种系统路径的信息。您还可以使用以下代码轻松获取当前 AppDomain 中加载的程序集列表:

Dim listOfAssemblies() As Assembly
listOfAssemblies 
= AppDomain.CurrentDomain.GetAssemblies()

 

此代码并非特定于 ASP.NET,但当从 ASP.NET 应用程序内部调用时,它将返回包含 AppDomain 中的程序集的数组,其中包括为您的页面生成的所有程序集。

ClientBuildManager 类没有多少信息一类的属性,CodeGenDir 属性除外,该属性返回与 HttpRuntime 的 CodeGenDir 属性相同的信息。但 ClientBuildManager 具有许多读取配置信息(如支持的浏览器)的方法和预编译应用程序的方法。Get 是该类中的一个方法,它返回一列应用程序的目录(在这些目录中监视那些会引发关闭 AppDomain 应用程序的重要更改)。这些目录是:App_Browsers、App_Code、App_GlobalResources、App_WebReferences 和 Bin。


构建资源管理器工具

® 用户界面的基于 COM 的插件。BHO 对于我在本专栏中构建的监视工具将是非常好的宿主环境,但可惜我偷了些懒,并没有这样做。因此我将我的工具编写为位于页面和浏览器之间的一个 HTTP 模块,它可查找查询字符串,如果是显式调用就可发挥作用。在 ASP.NET 应用程序中安装 HTTP 模块只需在 web.config 中增加一行语句,而且可以非常容易地开启和关闭安装:

<httpModules>
   
<add name="AspExplorerModule" type="Samples.AspExplorerModule" />
</httpModules>

 

图 6 显示了 Explorer HTTP 模块的大部分代码。该模块注册使用 PostMapRequestHandler 应用程序事件并与页面类挂接。PostMapRequestHandler 事件会在 ASP.NET 运行库确定了为请求提供服务所需的 HTTP 处理程序对象时触发。如果请求的查询字符串中包含 source=true 参数,并且处理程序是从 System.Web.UI.Page 继承的一个类,那么模块将开始工作。

图 6 中所示,模块所要做的就是使用不太常见的“页面”类方法 SetRenderMethodDelegate 为页面注册呈现委派 (rendering delegate)。当为页面指定了呈现委派时,所封装的方法会替代标准呈现处理。换言之,一旦安装了该模块,如果使用 test.aspx 进行调用,您将看到页面的标准输出;如果您使用 test.aspx?source=true 进行调用,您将看到模块可收集的所有与页面有关的运行时信息。

图 8 显示了实际运行中的 ASP Explorer。

领先技术-ASP.NET 页面的服务器端
图 8 实际运行中的 ASP Explorer 模块 (单击该图像获得较小视图)
领先技术-ASP.NET 页面的服务器端
图 8 实际运行中的 ASP Explorer 模块 (单击该图像获得较大视图)

示例页面分析

既然有了可使用的工具,那么让我们简要查看一下 ASP.NET 为每个 .aspx 文件生成的代码的结构。值得注意的是,如果没有 ASP.NET 运行库提供的分析和编译工具,您就必须亲自编写代码来运行 ASP.NET 页面!

图 5 中的 test_aspx 类)改写了 System.Web.UI.Page 类中的几个方法:FrameworkInitialize、ProcessRequest 和 GetTypeHashCode。ProcessRequest 没有什么变化,它只是调用它的基类方法。GetTypeHashCode 返回页面的散列代码,该代码可唯一标识页面的控件层次结构。当对页面进行编译时,会动态计算散列值,并将其作为常量插入到源文件。

最值得关注的是对 FrameworkInitialize 的改写。该方法控制页面的控件树的创建,并调入一个名为 __BuildControlTree 的私有方法。此方法使用与 .aspx 源文件中的 runat=server 标记相对应的控件的新实例来填充页面类的 Control 集合。__BuildControlTree 会分析所有服务器端标记并为每个标记构建一个对象。

<asp:textbox runat="server" id="TextBox1" text="Type here" />

 

以下是为上述标记获取的典型代码:

Private Function __BuildControlTextBox1() As TextBox
    
Dim __ctrl As New TextBox()
    
Me.TextBox1 = __ctrl
    __ctrl.ApplyStyleSheetSkin(
Me)
    __ctrl.ID 
= "TextBox1"
    __ctrl.Text 
= "Type here"
    
Return __ctrl
End Function

 

如果控件有事件处理程序或数据绑定表达式,会怎样?让我们首先来考虑带“单击”事件处理程序的按钮。您需要增加一行语句:

__ AddHandler __ctrl.Click, AddressOf Me.Button1_Click

 

对于数据绑定表达式 <%# … %>,除了使用了 DataBinding 事件,生成的代码与之类似:

AddHandler __ctrl.DataBinding, AddressOf Me.DataBindingMsg 

 

与处理程序相关的代码取决于绑定的控件的属性和要绑定的代码。对于 Label 控件的 Text 属性,代码类似于:

Public Sub DataBindingMsg(ByVal sender As ObjectByVal e As EventArgs) 
   
Dim target As Label = DirectCast(sender, Label)
   target.Text 
= Convert.ToString(领先技术-ASP.NET 页面的服务器端, _  
       CultureInfo.CurrentCulture);
End Sub

 

传递给 Convert.ToString 的表达式就是 <%# … %> 表达式中的代码。强制类型转换还取决于所涉及的类型。

如果存在母版页和主题,那么源文件的数量和依存关系列表就会增大,但借助 ASP Explorer 工具,您可以随意对其进行跟踪。


总结

ASP.NET 对其拥有的资源类型执行按需动态代码编译。此功能大大促进了 Web 应用程序的快速迭代开发,但需要 ASP.NET 才能将文件写到磁盘。编译文件夹是一个重要的文件夹,ASP.NET 的许多神奇之处都在此体现。您可以只是出于兴趣对此文件夹研究一番,有时却可以利用它来诊断和调试棘手的问题。当然,这里讨论的大部分功能是 ASP.NET 内部的功能,因此来说,这些功能在未来版本中可能会未经提醒即进行更改。 但截至目前为止,ASP.NET 2.0 的工作原理就是如本文所述的这样。顺便提一下,可以将 ASP Explorer 工具与 ASP.NET AJAX 应用程序一起使用,这一点也请放心。 该工具的运行效果非常好。


将您想向 Dino 询问的问题和提出的意见发送至  cutting@microsoft.com.
领先技术-ASP.NET 页面的服务器端 NEW: Explore the sample code online! - or - 代码下载位置: CuttingEdge2007_01.exe (276KB)

Dino Esposito 是 Solid Quality Learning 的培训讲师和顾问,也是《Programming Microsoft ASP.NET 2.0》(Microsoft Press,2005)一书的作者。Dino 先生定居于意大利,他经常在世界各地的业内活动中发表演讲。如果要与 Dino 联系,请发送电子邮件至 cutting@microsoft.com,或访问其博客,网址为:weblogs.asp.net/despos。

领先技术-ASP.NET 页面的服务器端  摘自 January 2007 期刊 MSDN Magazine.

相关文章: