1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > (转)深入剖析ASP.NET的编译原理

(转)深入剖析ASP.NET的编译原理

时间:2024-05-19 07:38:07

相关推荐

(转)深入剖析ASP.NET的编译原理

Microsoft 的Visual Studio为我们在应用开发中提供的强大功能,我们是有目共睹。借助该工具,是我们的开发 显得更加高效而轻松。从Microsoft把这个IDE的名字从 该为VS(比如原来的Visual ,现在的版本叫VS),可以MS对该IDE的期望和野心:MS要把它改造成一个万能的IDE。不过任何都有其两面性,对于我们广大的开发者来说,VS是我们的各种行为简单化,傻瓜化;但是在另一方面,他也会蒙蔽我们的眼睛,使我们对它背后做的事情视而不见。以我们的 Website开发为例,编程、编译、部署都可以借助VS,有了VS一切显得如此简单,每个人都会做,但是我想很多一部分人对一个 Website如何进行编译不会很了解。这篇文章就来谈谈背后的故事——是如何进行编译的。由于篇幅的问题整篇文章分两个部分:动态编译Dynamical Compilation和预编译(Precompilation)。

1.动态编译的过程

我们先来介绍在动态编译下的大体的执行流程:当收到一个基于某个page的request的时候,先判断该Page和相关的Source code是否编译过,如果没有就将其编译,如果已经编译,就是用已经Load的Assembly直接生成Page对象。

在这里有下面几点需要注意:

1). 动态编译是按需编译的,只会编译和当前Request相关的aspx和code。

2). 动态编译是基于某个目录的,也就是说会把被请求的page所在的目录的所有需要编译的文件进行编译,并生成一个Assembly。

3). 除了编译生成的Assembly外,动态编译还会生成一系列的辅助文件。

4). 对相关文件的修改,会导致重新编译,但是修改对当前的Request不起作用。也就是说如果你对某个aspx进行修改,那么对于修改后抵达的Request,会导致重新编译,但是对于之前的Request使用的依然是原来编译好的Assembly。

5). 编译生成的文件被放在一个临时目录中,这个目录的地址为Windows Directory\\Framework\v2.0.50727\Temporary Files。其具体的目录结构如下图所示:

在Temporary Files下的Artech.ASPNETDeployment是IIS中Virtual Directory的名称,以下两级目录的名称由Hash value构成,所以编译生成的文件就保存在c6f16246目录下。这个目录你可以通过HttpRuntime.CodegenDir获得。

Directory\\Framework\v2.0.50727\Temporary Files只是一个默认的临时目录,你可以在web config中的compilation section中设置你需要的临时目录。

<compilationtempDirectory="d:\MyTempFiles"/>

2.Sample

现在我用一个Sample来一探是如何进行动态编译的。

在这个Sample中,我建立了一个Website,在根目录下创建了两个Page:Default和Default2。

在两个子目录Part I和Part II下分别创建了两个Web page:Page1和Page2。在App_Code目录中创建了一个Utility的static class。下面是它的定义:

publicstaticclassUtility

{

publicstaticstringReflectAllAssmebly()

{

StringBuilderrefllectionResult=newStringBuilder();

foreach(AssemblyassemblyinAppDomain.CurrentDomain.GetAssemblies())

{

if(!assembly.FullName.Contains("App_Web"))

{

continue;

}

refllectionResult.Append(assembly.FullName+"<br/>");

Type[]allType=assembly.GetTypes();

foreach(TypetypeInfoinallType)

{

refllectionResult.Append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"+typeInfo.Name+"<br/>");

}

}

returnrefllectionResult.ToString();

}

}

内容很简单,对当前加载的所有相关的Assembly(这些Assembly的Fullname以App_Web打头)进行Reflection,列出所有的Type。这个ReflectAllAssmebly将在5个Web page(DefaultPage和两队Page1&Page2)的Page_Load事件中被调用。

protectedvoidPage_Load(objectsender,EventArgse)

{

this.Response.Write(Utility.ReflectAllAssmebly());

}

Default是列出所有4Page对应的Link以便我们访问它们,在我们再进行编译的情况下在IE中输入对应的URL来访问Default Page。(其他Page的Html中不具有真正的内容,是一个空的page.)

通过上面的显示,我们可以看到现在有一个Assembly:App_Web_wh7-uda5。该Asssembly定一个的Type有5个,_Default和default_aspx分别对应Default Page,而Default2和default2_aspxDefault2 Page的。FastObjectFactory_app_web_wh7_uda5是很重要的Type,我将会在后面对其进行深入介绍。正如我们在上面说过的,动态编译是按需编译,现在我们对Default Page进行访问,由于这次对该Website的第一次访问,所有需要的Source Code,包括aspx,code behind都要进行编译。在这个Sample中,虽然我们并没有访问Default2 page,但是我们说过,动态编译是基于目录的,由于Default Page和Default2 Page都直接置于根目录下,所以会把根目录下的所有文件编译到一个Assembly中。由于Page1和Page2位于子目录Part I和Part II之下,所以不会参与编译。除非我们下载对它进行Request。

我们现在来访问Part I下的Page1和Page2看看会有什么结果。我们会发现,两次Request获得的输出是一样的:

通过上面的输出我们发现,当前AppDomain中被加载的Assembly多了一个:App_Web_n1mhegpg。我们可以通过定义在该Assembly中的Type的命名可以猜出该Assembly是对Part I 目录进行编译产生的。Page1和Page2的编译后的Type name变成了part_i_page1_aspx& Page1和part_i_page2_aspx& Page2。此外我们看到,该Assembly中依然有一个FastObjectFactory的Type:FastObjectFactory_app_web_n1mhegpg。在这里我需要特别指出的是,名称的后缀都是通过 Hash算法得到的。

有了上面的理论和实验结果,我想这个时候,你肯定已经想到,如果我现在对Part II的Page1和Page2进行访问,输出结果会是什么样子了。

如果这个时候,你查看临时目录(Directory\\Framework\v2.0.50727\Temporary Files)中该Website对应的子目录,已将会看到生成了一些列的文件。

3.Page最终被转化成什么?

我们现在来看看通过编译,一个Page到底转换成什么。我们以Part I/Page1为例。通过上面的Sampe,我们知道它最终变成了两个Type:Page1和part_i_page1_aspx。

下面是Page1的定义:

publicclassPage1:Page,IRequiresSessionState

{

//Fields

protectedHtmlFormform1;

//Methods

protectedvoidPage_Load(objectsender,EventArgse)

{

base.Response.Write(Utility.ReflectAllAssmebly());

}

//Properties

protectedHttpApplicationApplicationInstance

{

get

{

returnthis.Context.ApplicationInstance;

}

}

protectedDefaultProfileProfile

{

get

{

return(DefaultProfile)this.Context.Profile;

}

}

}

下面是part_i_page1_aspx的定义

[CompilerGlobalScope]

publicclasspart_i_page1_aspx:Page1,IHttpHandler

{

//Fields

privatestaticobject__fileDependencies;

privatestaticbool__initialized;

//Methods

publicpart_i_page1_aspx();

privateHtmlHead__BuildControl__control2();

privateHtmlTitle__BuildControl__control3();

privateHtmlLink__BuildControl__control4();

privateHtmlForm__BuildControlform1();

privatevoid__BuildControlTree(part_i_page1_aspx__ctrl);

protectedoverridevoidFrameworkInitialize();

publicoverrideintGetTypeHashCode();

publicoverridevoidProcessRequest(HttpContextcontext);

}

在part_i_page1_aspx中定义了一个基于aspx的HttpHandler所需的所有操作,并且它继承了Page1。所以我们可以说Part I/Page1这个page 最终的体现为part_i_page1_aspx。进一步说,对Part I/Page1的Http Request,所要做的就是生成一个part_i_page1_aspx来Handle这个request就可以了。

4.FastObjectFactory

通过上面的一个简单的Sample,你已经看到每个Assembly中都会生成一个以FastObjectFactory作为前缀的Type。这是一个很重要的Type,从它的名称我们不难猜出它的作用:高效的生成对象。而生成的是什么样的对象呢?实际上就是基于每个aspx的Http request的Http handler,对于Part I/Page1就是我们上面一节分析的part_i_page1_aspx对象。

我们现在通过Reflector查看我们生成的App_Web_n1mhegpg中的FastObjectFactory_app_web_n1mhegpg是如何定义的。

internalclassFastObjectFactory_app_web_n1mhegpg

{

//Methods

privateFastObjectFactory_app_web_n1mhegpg()

{

}

privatestaticobjectCreate_ASP_part_i_page1_aspx()

{

returnnewpart_i_page1_aspx();

}

privatestaticobjectCreate_ASP_part_i_page2_aspx()

{

returnnewpart_i_page2_aspx();

}

}

通过上面的Code,我们可以看到在FastObjectFactory中定义一系列的Create_ASP_XXX(后缀就是Page 编译生成的Type的名称)。通过这些方法,可以快速生成对一个的Page。至于为什么会叫作FastObjectFactory,我想是因为直接通过调用这个静态的方法快速地创建Page对象,从而避免使用Reflection的late binding带来的性能的影响吧。

5.Preservation Files

进行每一次编译,会生成一系列具有.compiled扩展名的保留文件(Preservation File)。这个文件非常重要,我们现在来深入介绍这个样一个文件。

Preservation File这个文件本质上是一个XML。它是基于每个Page的,也就是每个Page都会有一个这样的Preservation File. 无论Page对应的Directory是怎样的,与之对应的Preservation File总会保存在根目录下,所以必须有一种机制保持为处于不同Directory的Page生成的Preservation File具有不同的文件名,不管Page的名称是否一样。所以Preservation File采用下面的命名机制:

.aspx.[folder-hash].compiled

其中是Page的名称,[folder-hash]是对page所在路径的Hash Value。这样做有两个好处。

保证处于同一级目录的所有Preservation File具有不同的文件名。 保证对于一个Http request可以找到Page对应的Preservation File。

下面这个Preservation File就是上面Sample中Part II/Page1.aspx对应的Preservation File,名称为default2.piled。我们来看看XML每个Item各代表什么意思。

<?xmlversion="1.0"encoding="utf-8"?>

<preserveresultType="3"virtualPath="/Artech.ASPNETDeployment/PartII/Page1.aspx"hash="fffffff75090c769"filehash="5f27fa390c45c52a"flags="110000"assembly="App_Web_hitlo3dt"type="ASP.part_ii_page1_aspx">

<filedeps>

<filedepname="/Artech.ASPNETDeployment/PartII/Page1.aspx"/>

<filedepname="/Artech.ASPNETDeployment/PartII/Page1.aspx.cs"/>

</filedeps>

</preserve>

有这段XML我们可以看到,所有的内容都包含在preserve XML element中,在他中间定义了几个重要的attribute.

virtualPath: Page的虚拟地址。 assembly:Assembly名称 Type:Page的编译后对应的Type(Http handler)。 hash: 一个代表本Preservation File状态的Hash value。 filehash: 一个代表本Dependent File状态的Hash value。

通过hash和filehash的缓存,可以判断自上一次使用以来,Preservation File和它所依赖的Dependent File是否被改动,如果真的被改动,将会重新编译。因为对于文件的任何改动都会导致该Hash value的改变。

此外,Preservation File的<filedeps>列出了所有依赖的文件,对于Page,一般是aspx和code behind。

6. 进一步剖析整个动态编译过程

现在我们来总结整个动态编译的过程:

Step1:当收到对于某个Page的Request,根据这个request对应的Url试着找到该page对应的Preservation File,如果没有找到,重新编译Page所在目录下的所有需要编译的文件,同时生成一些额外的文件,包括Preservation File。

Step2:然后解析这个Preservation File,通过hash和filehash判断文件自身或者是Dependent File是否在上一次编译之后进行过任何的修改,如果是的,则重新编译。然后重新执行Step2。

Step3:通过Preservation File 的assembly attribute Load对应的assembly(如果Assembly还没有被Load的话),通过type获知对应的aspx type,然后借助FastObjectFactory的对应的Create_ASP_XXX创建这个type。这个新创建的对象就是我们需要的Http Handler,在之上执行相应的操作把结果Response到客户端。

现在我们来谈谈另外一种重要的编译方式:预编译(Precompilation)。

1.为什么要进行预编译

2.0的编译方式大体可以分成两种:动态编译和预编译,要回答为什么要进行预编译,我们先要看看动态编译有什么不好的地方。我们回顾一下上一篇介绍的进行动态编译的简单的流程:当来自Brower的一个基于aspx的Http request抵达Web server,IIS handle这个request,通过分析注册在IIS中的Application Mapping,将Request 传给aspnet_isapi.dll ISAPI extension。ISAPI extension通过HttpRuntime进入Http Runtime Pipeline,HttpRuntime为每个Request创建一个单独的HttpContext对象,用于保存request的Context信息。在Http Runtime Pipeline中,Http request会被注册的一系列的Http module处理,比如OutputCache Module,Session Module,Authentication Module,Authorization,ErrorHandler Module等等。在Pipeline的终端,需要需要根据request创建对应的HttpHandler对象来处理该Request,并生成结果Response到Client。对于一个基于Aspx的Http request,对应的Http handler对象一般就是一个System.Web.UI.Page对象。

会先判断对应的Page type是否存在于被Cache的Assembly中,如果存在,直接创建Page对象,否则会先对该Page的相关的Source code (包括code behind,html等等) 进行编译,我们也说过这种编译是一Directory为单位的,也就是说,处于同一个Directory下的需要编译的文件会被编译成到同一个Assembly中。编译生成的Assembly会被Cache,用于后续的Request。

正是因为对资源的首次访问会导致一次编译(这样说不太准确,因为动态编译是以directory为单位进行的,应该对对某个Directory下的资源进行首次访问),这样会严重降低Web Application的响应速度。所以我们为了避免这种情况,需要预先对web site进行编译,所以提高web site的响应是进行预编译的最重要的原因。

同时动态编译就以为着Web server上放置的是Source code,而且他们是可被修改的。而对于一个开发完毕的Web Application,我们更希望以Binary Assembly的方式进行部署,这样Server上部署的都是Binary Assembly,不怕被别人篡改而导致系统的崩溃,从知识产权来讲,也更利于保护商业秘密。这也是我们为什么要进行预编译的另一个原因。

下面我们就来讲讲如何进行预编译,以及与编译背后的原理。同时在这里我需要特别提出的是,在上一部分讲的一些术语和原理,比如Preservation file,FastObjectFactory,同样适用于预编译,重复的内容,在这里就不必再介绍了。同时我也将沿用上一部的Sample。如果想看看相关的内容,请参阅[原创]深入剖析的编译原理之一:动态编译(Dynamical Compilation)。

2.In Place Pre-compilation V.S. Pre-compilation for Deployment

对于预编译,有可以分为In Place Pre-compilation和Pre-compilation for Deployment,In Place Pre-compilation很简单,实际上就是把整个Web site编译到我们一个临时的目录下面,这个临时目录也就是我们在介绍动态编译提到的那个临时目录。而且这个编译的方式,包括生成的文件也和动态编译完全一样,唯一不同就是编译的时间:预先编译,编译的范围:整个Web site。这种编译就是你常用的在VS的build。这种编译方式一般用于开发阶段。

为了部署为目的的编译是我们今天讨论的重点,下面我们就着重来讨论Pre-compilation for Deployment。

注:在的编译都是通过一个叫做aspnet_compiler的工具执行的,该工具随 2.0一起发布,你完全可以利用此工具以命令行的方式的执行编译,并通过传递不同的命令行开关设置不同的编译选项。该工具被置于了VS中,使你可以利用VS进行可视化的编译。

3.Non-updatable Pre-compilation V.S. Updatable Pre-compilation

2.0为我们提供了几种不同方式的预编译和部署。为了弄清楚这些预编译和部署方式,我们先来回顾一下 1.x下的编译方式。我们知道在 1.x时代对整个Web site进行编译,实际上我们只会对所有C#和等后台代码进行编译,并生成一个单一的Assembly。而Web page的aspx是不会参与编译的。所以当我们访问一个Web page的时候,必须对aspx进行动态编译。

这一切之所以能够进行是因为Web page采用的是aspx + code behind的模式。

<%@PageLanguage="C#"AutoEventWireup="false"

Codebehind="Default.aspx.cs"

Inherits="Default"%>

publicpartialclassDefault:System.Web.UI.Page

{

protectedvoidPage_Load(objectsender,EventArgse)

{

}

}

从上面我们可以看到aspx和Code behind是一种继承的关系,aspx继承和它对应的Code Behind。可以把Code behind和aspx分开进行编译,把它们编译到不同的Assembly中。我们就是上面的Code为例,

我们现在若对该Web site进行编译的话,Default.aspx.cs会被编译到一个Assembly中,假设这个Assembly为App_Web.dll. 我们把该Dll和aspx部署到Production Server上。如果我们现在访问defaut.aspx。

会对aspx进行动态编译,生成的Assembly可以暂时成为App_Web_aspx.dll。对于Default.aspx,如果我们如C#代码来描述的话,应该像下面一样定义:

publicclassdefault_aspx:Default

{

}

这种编译方式,我自己把它叫做对asXx的动态编译。在2.0 中也沿用了这种编译方式。这种编译方式的主要特征是对Code behind和所有的后台代码进行预编余,aspx(确切地说应该是asXx:asax,asmx,asax等)原样部署。由于这种方式的预编译,asXx是可以修改的(当然这种修改是有一定限制的,因为code behind已经编译好了,所以这种修改只可能是和code behind无关的修改),所以又叫做Updatable Pre-compilation。

除了Updatable Pre-compilation之外,还提供另外一种高效的预编译方式,Non-updatable Pre-compilation,之所以叫做不可修改的预编译,这是因为:这种编译方式把asXx、Code behind、后台代码甚至是部分Resource都进行预编译,从而避免了运行时对asXx的动态编译,从而最大程度地提高了整个Web site的响应。在部署的时候,我们除了把生成的Assembly进行部署之外,所有的通过编译生成的asXx也必须进行部署。 不过需要特别说明的是,此时的asXx文件仅仅是一个占位的文件而已,它里面不具有任何HTML。

4.Partial class

在 1.x,由于采用的aspx+ code behind的机制,对于任何一个Web page或者其他 基于axXx的对象来说,都是由两个文件、两个class组成。两个文件是指axXx和code behind,两个class是指Code behind定义的继承自System.Web.UI.Page的class,和一个继承自它的由axXx生成的class。

对于使用过 1.x来说,一定会很熟悉这样一种情况:对于每个在aspx中通过HTML定义的Server Control,在Code behind中必须具有一个对应的protected成员,否则你不能通过编程的方式访问这个Server control。以不同方式呈现的同一个Server control通过ID关联起来,如果在Code behind中改了Server control的ID,Server control的Server端的Event handler将会失去原有的作用。

但是在 2.0来说,这种情况发生了改变,在aspx中的Server control在Code behind中却没有相应的成员变量,但是我们可以毫无障碍地访问到每个Server control。这使得我们的code behind更加简洁,通过避免了Server control在aspx和code bebind中的不匹配的问题。这一切都得益于.NET Framework 2.0提供的partial class的机制:把同一个class分布于不同文件中进行定义。有了这个概念,我们来看 2.0的code behind机制。

比如我们有这样的一个Page:

<%@PageLanguage="C#"AutoEventWireup="true"CodeFile="Default.aspx.cs"Inherits="_Default"%>

<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<htmlxmlns="/1999/xhtml">

<headrunat="server">

<title>Default</title>

<linkhref="Style.css"rel="stylesheet"type="text/css"/>

</head>

<body>

<formid="form1"runat="server">

<div>

<asp:Buttonrunat="server"ID="btnRefresh"Text="Refresh"OnClick="btnRefresh_Click"/>

</div>

</form>

</body>

</html>

Code behind如下:

publicpartialclass_Default:System.Web.UI.Page

{

protectedvoidPage_Load(objectsender,EventArgse)

{

}

protectedvoidbtnRefresh_Click(objectsender,EventArgse)

{

this.Response.Write("Theclickeventof\"Refresh\"buttonisfired");

}

}

而实际上,会为我们创建一个隐藏的.cs文件(这个文件有人 把它称之为Sibling partial class):

publicpartialclass_Default:IRequiresSessionState

{

protectedButton_btnRefresh;

}

这个文件会随着aspx文件的改变而动态变化,所以code behind中的Server control永远和aspx中的Server control是完全匹配的。所以我们说 2.0的Page是由3个文件、两个class组成的。

5.编译的粒度和Assembly的命名

到现在为止,我们所讲的的预编译都是以Directory为单位的,同一个Directory下的所有需要编译的文件被编译到同一个Assembly中。还支持以Page为单位的预编译,也就是每个Page编译成一个Assembly。

在默认的情况下,预编译生成的Assembly名称是随机生成的,也就是每次生成的Assembly都具有不同的name。所以我们在部署Web site的时候,一般需要把原来的Assembly删掉,再部署新的Assembly。不过为我们提供了另外一种选择,使得每次编译生成的Assembly具有相同的名称,这样我们部署的时候就可以直接把新的Assembly拷贝到Production Server上,自动覆盖掉同名的Assembly。

6.Sample

我们沿用上一部份是用的Sample,我们通过采用不同的预编译方式看看程序将如何运行。

6.1 Non-updatable Pre-compilation

我们采用如上图所示的默认的发布方式, 将会进行Non-updatable Pre-compilation。浏览目标文件夹,我们会发现 如下的文件结构.

除了多了一个Bin目录和PrecompiledApp.config之外,整个结构和Source code中的结构完全一样。通过上面的分析,我们知道这种预编译方式是将asXx、code behind、后台代码已经Resource一起编译成Assembly。我们说过对于这样的预编译方式,aspx仅仅是一个站位的文件而以,其中HTML已经没有任何意义了,那么对于编译后的aspx中到底是什么东西呢。我们来一探究竟。打开每个aspx都是一段如下如下一样文字,并无任何HTML。

Thisisamarkerfilegeneratedbytheprecompilationtool,andshouldnotbedeleted!

PrecompiledApp.config里面具有一段简短的configuration,表明version和是否可以进行进一步的修改。

<precompiledAppversion="2"updatable="false"/>

所有的Assembly被编译到Bin目录中,我们来看看到底生成了一些什么样的文件在Bin目录中。

在Bin目录由两类文件构成:Assembly和以complied作为扩展名的Preservation file。Preservation file的内容和作用在第一部分已经详细介绍过了,相信大家不会感到陌生。Preservation file在这里和动态编译所起的作用一样。唯一有一点不同的是,他的结构更加简洁,去掉的Dependence file的列表,因为对于Non-updatable Pre-compilation来说,每个Page的以来的文件都是不可更改的。

<preserveresultType="6"virtualPath="/Artech.ASPNETDeployment/App_Code/"hash="439abe7d"filehash=""flags="140000"assembly="App_Code"/>

我们来运行以下程序,和动态编译情况下的输出结果比较,看看有什么不同。我们照例先运行Default Page。

输出的结果印证我们前面的讨论:处于同一个目录下的Default 和Default2被编译到同一个Assembly中,关注于处理逻辑的codebehind的class name为Default和Default2,关注与可视化界面render的aspx对应的class name被加上的_aspx后缀,如果对default_aspx和default2_aspx进行Reflect的话,你会发现他们分别继承Default和Default2,而后者直接继承自System.Web.UI.Page。所以default_aspx和default2_aspx是真正的意义上基于Web page的Http handler。像动态编译一样,预编译生成一个基于Assembly的FastObjectFactory Type,对该对象的描述请参照第一部分。

有了前面的理论基础,相信大家已经猜到这时候,我浏览Part I下的Page1和Page2时的输出是什么 样子,由于预编译是以目录为单位的,我们对Part I下的任何一个page的访问,都会加载相同的Assembly,所以此时对这两个Page的访问会得到一样的输出结果:

6.2 Updatable Pre-compilation

接下来我们来对Web Page进行Updatable Pre-compilation,相关的编译设置如下:选择Allow this precompiled site to be updatable。

生成的文件及其结构和进行Non-updatable Pre-compilation,不同的又一下3点:

PrecompiledApp.config:updatable被设置为true。

<precompiledAppversion="2"updatable="true"/> asXx和我们进行开发时内容一样,你如aspx包含的就是HTML,我们可以在部署之后对他们进行和code behind无关的修改。 Preversation file中有加上了Page对应的dependence file列表。

<?xmlversion="1.0"encoding="utf-8"?>

<preserveresultType="9"virtualPath="/Artech.ASPNETDeployment/App_GlobalResources/"hash="439abe7d"filehash="ff21249472dbf6cb"flags="140000"assembly="App_GlobalResources"resHash="1cba48dd56e28538"/>

我们来运行一下Web site,看看现在的输出结果又有何不同。首先打开Default Page:

通过上图,我们发现此时加载了两个相关的Assembly。我们来分析一下为什么会这样。在分析Updatable Pre-compilation时,我们说过:asXx是不会才与编译的,只有他们的code behind, 所有的后台代码,资源文件才会本编译。对于一个page 来说,page的code behind被编译到Assembly中,aspx则不会。Aspx在运行时实行动态编译,所以aspx是可被修改的。在本例中,我们访问Default Page,先对aspx进行编译,其对应的class name为default_aspx,由于default_aspx继承与Default,并且Default存在于预编译生成的Assembly中,所以这个Assembly被加载进来。

由于同一个page最重本编译到两个不同的Assembly中,所以我们此时访问Part I中的Page1或者Page2,又会有两个Assembly被加载进来:

6.3 以Page为单位进行预编译

前面我们进行的都是以directory为单位的预编译,现在我们缩小编译的粒度,以Page为单位进行编译。我们选择了“Use fixed naming and single page assemblies”选项。那么现在进行的是 基于单个page的non-updatable pre-compilation。通时由于采用的是fixed naming的编译方式,每次进行编译生成的Assembly的名称都是一样的。

现在我们来看看,编译之后生成的Assembly:

我们看到编译器为每个Page生成了一个单独的Assembly。此时运行程序,你看到的又将不同。如果此时你访问Default Page,你将看到:

是不是和上面不同,Assembly只有Default对应的两个Type,没有了处于同一个目录下的Default2的Type。因为Default2有它独自的Assembly .所以你该会想到,如果我们现在每访问一个没有被访问过的Page,就会有一个新的Assembly被加载。比如我么访问Part I下的Page1:

6.4编译强类型的Assembly

我们知道可以通过一个Public key/Private key pair对Assembly进行签名,进而把它部署到GAC中,我们来看看如何做。

首先我通过SN.exe生成Public key/Private key pair并保存到一个文件中(比如D:\MyKey.keys),然后进行如下的编译设置

那么我们进行编译就会生成强类型的Assembly。我们可以运行我们的程序来证明:

每个Assembly有具有一样的PublicKeyToken,因为我们使用的一样的Public key/Private key pair进行对每个Assembly签名的。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。