Fritz Onion

当 ASP 第一次发布时,Web 编程还比较困难,因为需要 IIS 来处理 ASP 页。后来,ASP.NET 2.0 和 Visual Studio® 2005 通过引入网站开发模型使一切工作都变得容易了。借助该网站模型,您不必在 Visual Studio 中创建新项目,而是可以指向一个目录并开始编写网页和代码。此外,您还可以使用内置的 ASP.NET Development Server 快速测试站点,ASP.NET Development Server 将 ASP.NET 寄宿在一个本地进程中,并消除了必须安装 IIS 才能进行开发这一先决条件。该网站模型的魅力在于您在开发 Web 应用程序时无需考虑打包和部署。需要其他类时怎么办?向 App_Code 目录添加一个 .cs 文件即可开始编写。希望将可本地化的字符串存储在资源文件中时怎么办?向 App_GlobalResources 目录添加一个 .resx 文件并键入字符串。一切都顺顺当当;您根本就不必考虑编译和部署方面的事情。
在准备进行部署时,您有多种可选方案。最简单的方案是将文件复制到主运行服务器并按要求编译每一个文件(和在测试环境中一样)。第二种方案是使用 aspnet_compiler.exe 实用工具将应用程序预编译为二进制版本,之后将只剩下要放到服务器上的一组程序集、静态内容和配置文件。第三种方案也使用 aspnet_compiler.exe,但要创建一个可更新的二进制部署,其中 .as*x 文件保持不变(并且可修改),而所有代码文件都编译为二进制程序集。
这似乎涵盖了每一种可能的情况,开发人员可以一心一意地编写 Web 应用程序,而在以后实际部署时再作打包和部署决定。不过,此模型也遭到了相当大的反对,特别是那些习惯了自己开发的 Web 项目是在实际项目文件中指定的实际项目的开发人员的反对,这些项目允许注入生成前和生成后函数、从生成过程排除文件以及使用命令行开关在调试和发布版本之间进行切换等操作。有鉴于此,Microsoft 迅速推出了 Web 应用程序项目(即 WAP),最初它是作为 Visual Studio 2005 的插件发布的,现在包含在 Visual Studio 2005 Service Pack 1 (SP1) 中,Visual Studio 2005 Service Pack 1 (SP1) 可从 msdn.microsoft.com/vstudio/support/vs2005sp1 下载。
WAP 可替代与 Visual Studio .NET 2003 Web 项目模型非常接近的网站模型。新的 WAP 模型会在生成过程中编译所有源代码文件,并在本地的 /bin 目录中生成一个用于部署的程序集。WAP 还使得增量采用 ASP.NET 2.0 引入的新的分部类代码隐藏模型变得更加容易,因为现在您可以打开 Visual Studio .NET 2003 项目,并且在转换过程中只修改 .sln 和 .csproj(或 .vbproj)文件。然后可将每个文件及其代码隐藏类转换为与项目中任何其他文件都无关的新的分部类模型(操作方法是:在解决方案资源管理器中右键单击各个文件并选择“转换为 Web 应用程序”),也可以让它们仍然使用旧模型。这与将 Visual Studio .NET 2003 Web 项目转换为网站模型大不相同,转换为网站模型会同时转换所有文件,并且不支持增量采用。
最后,还有一个称为“Web 部署项目”(本专栏的主题)的新项目类型,它引入了许多既针对网站项目又针对 Web 应用程序项目的附加部署选项。 Web 部署项目弥补了既针对网站应用程序又针对 Web 应用程序项目的部署选项中的遗留漏洞,并且可以简单而又可扩展地实现几乎任何部署方案。 为确切了解这一新项目类型增加了哪些内容,我们先来回顾一下在 Web 部署项目推出之前的情况。

在 ASP.NET 2.0 中部署
使用网站模型生成应用程序时,您可以选择对部署站点进行预编译。通过 Visual Studio 2005 中的“生成”|“发布”菜单或直接通过命令行实用工具 aspnet_compiler.exe,您可以访问预编译实用工具。图 1 显示了 Visual Studio 所显示的此工具的界面。
图 1 网站发布实用工具 (单击该图像获得较大视图)
使用发布实用工具时必须作出的第一个决定是 .as*x 文件在部署后是否可更新(在 aspnet_compiler.exe 命令行实用工具中使用 -u 开关的“允许更新此预编译站点”选项)。 此决定取决于在部署后是否希望能够在不重复整个部署过程的情况下对网页进行较少更改。事实上,您可能希望明确禁止对已部署网页进行任何修改,并要求所有修改都要遵循标准的部署(也希望遵循标准的测试)过程,在这种情况下,应选择将站点发布为不可更新。
将站点发布为不可更新时,您可以完全删除所有 .as*x 文件,而只发布二进制程序集(以及配置文件和静态内容)。不过,如果没有物理文件,ASP.NET 将无法确定哪些类要用于哪些端点请求。例如,如果您的应用程序收到一个请求 Page1.aspx 的请求,而您已经使用了不可更新的二进制部署,则磁盘上很可能没有任何 Page1.aspx 文件,并且现有配置文件中没有任何内容来指示部署到 /bin 目录的程序集集合中哪个类应是该请求的实际处理程序。为弥补这一缺陷,编译过程还将生成一个 .compiled 文件集合,这些文件以简单的 XML 格式包含端点-类型映射和文件依赖关系信息,同时这些文件必须与所部署站点的 /bin 目录中的二进制程序集一起发布。例如,如果应用程序中原来有一个名为 Page1.aspx 的页,则 aspnet_compiler.exe 实用工具会生成一个名为 page1.aspx.cdcab7d2.compiled(哈希代码不定)的文件,其中包含以下 XML:
<?xml version="1.0" encoding="utf-8"?>
<preserve resultType="3"  
          virtualPath="/SampleWebSite/Page1.aspx" 
          hash="8a8da6c5a" filehash="42c4a74221152888" 
          flags="110000" assembly="App_Web_aq9bt8mj" 
          type="ASP.page1_aspx">
  <filedeps>        
    <filedep name="/SampleWebSite/Page1.aspx" />
    <filedep name="/SampleWebSite/Page1.aspx.cs" />
  </filedeps>
</preserve>
使用此实用工具发布网站时必须作出的另一个重要决定是确定生成的程序集的打包粒度。通过选中“使用固定命名和单页程序集”(或在 aspnet_compiler.exe 命令行实用工具中使用 -fixednames),既可为站点中的每个目录创建单独的程序集,又可为站点中的每个可编译文件创建单独的程序集。作出该决定并不像您可能想像的那么容易,因为每个选项都有其潜在问题。如果决定不使用 -fixednames 选项,则每次发布应用程序时都会生成一组全新的程序集,并且它们的名称与之前发布的程序集不同。这意味着部署更加复杂,因为在部署新的程序集之前必须删除主运行服务器上所有以前发布的程序集,否则在处理下一个请求时将生成冗余的类定义错误。使用 -fixednames 选项可以解决此问题,因为每个文件都将与命名清晰的程序集对应,而这些程序集在一次编译和下次编译中不会发生变化。不过,如果站点规模较大,则为每个网页、控件和母版页分别生成单独的程序集,很明显意味着您要管理成百上千个程序集的发布。Web 部署项目非常圆满地解决了部署中程序集粒度这一问题,如下所示。
您还可以将程序集签名引入编译过程,以便创建具有强名称的不同版本的程序集,如果需要这也适用于全局程序集缓存 (GAC) 中的部署。通过使用 -aptca 选项,您可以使用程序集级别的属性 AllowPartiallyTrustedCallers 来标记生成的程序集,在将任何程序集部署到 GAC 并且以低等或中等信任级别运行 ASP.NET 的情况下,这是必要的。(请注意,此属性应仅应用于已证明不会暴露任何安全漏洞的程序集,因为如有漏洞,使用此属性可能招致引诱攻击。)
有关发布站点的另一个细节是如果决定使用 Web 应用程序项目而不使用网站模型,则“生成”|“发布”对话框的外观将大不相同,如图 2 所示。Web 应用程序项目假定您希望将应用程序发布为可更新的 .as*x 文件和预编译的源文件(开发中它所使用的同一模型),因此仅针对二进制的部署选项不可用。此实用工具实质上更接近于“复制网站”实用工具(随网站一起提供)而不是“发布网站”实用工具,因为它需要复制由标准生成过程生成的文件。
图 2 Web 应用程序项目的发布 Web 实用工具 (单击该图像获得较大视图)
从技术上讲,即使您使用 Web 应用程序项目,也不会限制您使用仅针对二进制(不可更新)的部署。其实,WAP 生成的输出是一个有效的网站,然后您可以传递 aspnet_compiler.exe 实用工具来生成创建二进制部署。幸运的是,您只是不能从 Web 部署项目调整过的 Visual Studio 2005 界面调用它而已。

Web 部署项目
那么迄今为止所有现有的编译和部署选项中缺少什么呢?主要缺少两种功能:控制程序集命名(特别是为了进行部署)的功能,以及将所有输出的程序集合并为一个程序集从而简化部署的功能。Web 部署项目可以解决这两个问题。但或许更重要的是,它们还与网站应用程序和 Web 应用程序项目的部署问题中的许多遗留问题有关。
它们的核心是,Web 部署项目(可从 msdn2.microsoft.com/aa336619.aspx 下载)代表的只是向您解决方案中添加的另一个项目类型。与所有 Visual Studio 项目文件一样,Web 部署项目也是可在 IDE 中直接编译或从命令行运行的 MSBuild 脚本。不过,Web 部署项目包含用于编译和打包网站(或 Web 应用程序项目)的生成命令,而不指定要编译的源代码文件集合。这意味着它们会调用 aspnet_compiler.exe 实用工具(以及其他实用工具)来创建特定 Web 应用程序的部署。Web 部署项目是作为 Visual Studio 插件包提供的,其中包含了一个用于注入新项目的易用菜单项和一个用于控制所有可用设置的完整属性页集。若要向现有应用程序中添加新项目,可右键单击现有网站(或 Web 应用程序项目),然后选择“添加 Web 部署项目”项,如图 3 所示。此操作将把一个包含 MSBuild 脚本的新 .wdproj 文件添加到您的解决方案中,并会生成您所创建的应用程序的部署。
图 3 添加 Web 部署项目 
图 3a   
图 3b   (单击该图像获得较大视图)
图 3c   
将 Web 部署项目添加到您的解决方案之后,您就可以通过访问项目文件的属性页来精确控制项目的用途,如图 4 所示。新部署项目的默认设置是以可更新模式部署应用程序,所有 .as*x 文件都将保持不变,源文件则编译为部署在顶级 /bin 目录中的一个程序集。不管源应用程序使用网站模型还是使用 Web 应用程序项目模型,这些部署项目的作用都是相同的,这意味着无论您现在选择哪个开发模型都不会影响您的部署选项。Web 部署项目最重要的功能之一是它能够将所有部署都配置为二进制(不可更新)- 一个程序集,您可以为该程序集选择名称。使用此部署模型意味着,您只需将一个程序集放到活动站点的 /bin 目录中即可更新整个站点,并且在部署或处理导致错误的已部分部署的站点之前无需删除现有程序集。为端点映射部署 .compiled 文件仍是必需的,但只有当您在站点中添加、删除或移动页时这些文件才会发生变化。
图 4 Web 部署项目属性页 (单击该图像获得较大视图)
Web 部署项目提供了部署灵活性,使您可以在作出打包和部署决定时无需考虑 Web 应用程序的实际生成过程。借助 aspnet_compiler.exe 实用工具,ASP.NET 2.0 的原始版本部分地实现了开发和部署之间的这种独立性,但由于执行部署时的各种约束从未完全实现。Web 部署项目则已完全实现了开发和部署的分离,有关应用程序如何生成的决定将不再影响部署选择。

合并程序集
Web 部署项目的功能主要是对通过 MSBuild 任务和新界面提供的现有实用工具进行重新打包,但除此之外还提供了几个全新功能。其中最引人关注的功能是程序集合并功能。
安装 Web 部署项目时,您会发现安装目录(默认情况下是 %PROGRAMFILES%\MSBuild\Microsoft\WebDeployment\v8.0)中有一个名为 aspnet_merge.exe 的可执行文件。该可执行文件能够提取预编译站点的多个程序集输出并将这些程序集合并为一个程序集。如果选中 Web 部署项目中的合并选项,则该实用工具即可集成到生成脚本中。为了说明该实用工具的功能,我们来看一个没有可更新开关的预编译网站的输出,如图 5 所示。该输出的源应用程序包含两个子目录、一个顶级 global.asax 文件、一个在 App_Code 中定义的类以及一个用户控件。最终的编译结果是五个不同的程序集和一个 .compiled 文件集合。如果在此目录上运行 aspnet_merge.exe 实用工具(使用 -o 开关)来请求一个程序集输出(如图 5 底部所示),结果将是一个可管理性大大提高的单一程序集,其名称可以随意指定。
图 5a 使用 aspnet_merge.exe 实用工具 (单击该图像获得较大视图)
图 5b(单击该图像获得较大视图)
虽然随 Web 部署项目一起提供的 aspnet_merge.exe 实用工具和相应的 MSBuild 任务是新的,但自从微软研究院将 Microsoft® .NET Framework 1.1 包装成名为 ILMerge 的实用工具以来,用于合并程序集的基础技术实际上就已经诞生了。ILMerge 的最新版本可从 research.microsoft.com/~mbarnett/ILMerge.aspx 下载。此实用工具现已直接集成到 aspnet_merge.exe 中,用于执行与合并程序集相关的所有繁重任务。其实,程序集合并是一项相当复杂的任务。您需要考虑签名、版本控制、其他程序集级别的属性、嵌入式资源和 XML 文档,同时还要管理冲突类型名称的详细信息等内容。ILMerge 实用工具可以为您管理所有这些内容,并用开关来控制有关该过程的各种决定。使用该实用工具还可以将 .exe 程序集转换为 .dll 程序集以便打包。例如,假定您有三个程序集:a.dll、b.dll 和 c.exe,并要将它们合并为一个库程序集。只要类型名称中没有冲突,下面的命令行就会生成一个新库 d.dll,其中包含在 a.dll、b.dll 和 c.exe 中定义的所有类型:
ilmerge.exe /t:library /ndebug /out:d.dll a.dll b.dll c.exe

可插入配置文件
Web 部署项目提供的另一个全新功能是创建可插入配置文件。部署 Web 应用程序时,要确定一种方法来管理开发与部署之间配置文件的差别是一个常见问题。例如,您的一个本地测试数据库可能用于运行站点,另一个数据库用于暂存服务器,还有一个数据库用于主运行服务器。如果将连接字符串存储在 web.config 中(通常是在 connectionStrings 部分),那么在将应用程序推送到暂存服务器或实际工作环境时,就需要通过某种方式来修改这些字符串。Web 部署项目通过一个称为 ReplaceConfigSections 的新 MSBuild 任务提供了一个针对此问题的完全解决方案。
通过此任务,您可以指定根据解决方案配置独立存储特定配置部分内容的独立文件。例如,您可以创建一个 debugconnectionstrings.config 文件来存储如下所示的 connectionStrings 配置部分的调试版本:
<connectionStrings>
    <add connectionString="server=localhost;database=sales;
                           trusted_connection=yes" name="sales_dsn"/>
</connectionStrings>
与此类似,接下来您可以为所定义的每个解决方案配置(发布、暂存等)创建单独的文件,并用它们各自部署环境所需的连接字符串进行填充。对于发布配置,可将文件命名为 releaseconnectionstrings.config,并按如下方式填充:
<connectionStrings>
    <add connectionString="server=livedbserver;database=sales;
                           trusted_connection=yes" 
                           name="sales_dsn"/>
</connectionStrings>
接下来,您可以对由 Web 部署项目添加的 MSBuild 脚本进行配置,以说明应替换主 web.config 文件中的哪些配置部分以及将提供替换内容的源文件。您可以手动修改脚本,但 Visual Studio 中生成脚本的属性页提供的漂亮界面可以为您代劳,如图 6 所示。在此例中,您是在设置调试解决方案配置的属性,因此选中“启用 Web.config 文件部分替换”选项并指定要随该文件一起替换为相应内容的部分:
connectionStrings=debugconnectionstrings.config
图 6 用于设置配置文件部分替换的属性页 (单击该图像获得较大视图)
您可使用同一对话框页以相应的文件来设置发布解决方案配置(以及我们已定义的任何其他配置)的配置替换。
在随后运行生成脚本时,ReplaceConfigSections 任务会从任何关联的配置文件中提取内容,并替换相应配置部分的内容,同时创建一个放到部署目录的新 web.config 文件。这种配置文件替换功能意味着您可以借助由源代码管理进行版本控制的文本文件,以一种易于管理的方式来维护部署环境之间的配置差异,并且不必使用提醒您在部署时更改连接字符串的粘滞便笺。应予以强调的是,此功能适用于配置文件的任何部分,包括自定义部分,因此如果其他配置部分(例如,appSettings)有差异,您也可以使用此生成任务轻松指定这些差异。

创建可重用用户控件
Web 部署项目有一个有趣的附带应用程序,该应用程序解决了一个已困扰 ASP.NET 开发人员多年的问题,即如何创建可重用用户控件以便进行跨应用程序共享。用户控件实质上就是复合的自定义控件,其子控件位于 .ascx 文件中。能够将设计器用于布局控件和添加处理程序对于大多数开发人员来说都是一个巨大帮助,因为在感觉上这和生成网页几乎完全相同,不同的是得到的 .ascx 文件可作为控件包含在任何网页中。而一直存在的不足是需要在应用程序的目录中保留实际的 .ascx 文件才能真正使用它。尽管已有用于实现跨应用程序共享 .ascx 控件的技术,但这些技术通常都涉及许多繁琐事务(如在应用程序之间创建共享虚拟目录或在请求时搜集由 ASP.NET 生成的临时程序集),并且从未让人感到满意。
版本 2.0 中引入的 aspnet_compiler.exe 实用工具提供了一个比较好的解决方案。借助此编译器,您可以创建一个仅由用户控件组成的网站,并以不可更新模式发布该站点,以便生成可重用程序集。获得生成的程序集后,您可以将其部署到任何 Web 应用程序并像引用自定义控件那样引用该用户控件(而不是像针对 .ascx 文件那样使用 src 属性)。此技术的唯一缺点是,您要么必须接受由编译过程生成的随机命名程序集,要么必须选中编译器中的 fixednames 选项,以便为站点中的每个母版页各生成名称固定的程序集(而不是为整个集合只生成一个程序集)。
Web 部署项目提供了用于创建真正可重用的用户控件程序集的决定性步骤。您可以使用只包含用户控件的网站,并通过添加 Web 部署项目来创建具有所选名称的单一输出程序集。创建一个要部署到 GAC 的签名程序集会更简单直接,这样无需在每个 /bin 目录中重新部署该程序集即可实现跨多个应用程序的控件共享。

总结
Web 部署项目的推出令人非常满意地完善了用于部署 ASP.NET 应用程序的工具集。现在可以用从所有源到所有二进制的任何方式部署应用程序,并且可以完全控制二进制程序集的生成、打包和命名。此外,Web 部署项目还提供了一个解决方案以便根据目标版本替换配置文件的各部分,并解决了可重用用户控件的分发问题。正在构建和部署 ASP.NET 应用程序的任何人肯定都会发现 Web 部署项目的某些方面非常有用,足以吸引他们立即开始使用 Web 部署项目。

将您想向 Fritz 询问的问题和提出的意见发送至:xtrmasp@microsoft.com xtrmasp@microsoft.com.

posted on 2010-03-28 14:09  yxbsmx  阅读(390)  评论(0编辑  收藏  举报