程序集:定位、绑定和部署






4.89/5 (55投票s)
2005 年 11 月 10 日
9分钟阅读

282751
如何提前规划服务包、升级和热修复。
引言
本文描述了 CLR 如何定位和绑定程序集,以及如何在需要时(例如在部署阶段)更改默认行为。
任何处理 .NET 程序集的开发人员和系统管理员,特别是商业应用程序,都必须熟悉这些主题。这些知识是规划服务包、升级和热修复的最佳方式。
.NET Framework 充满了与程序集部署、定位和绑定相关的大量术语和功能。
下面是一个简短列表
- 静态和动态加载
- 公共和私有
- GAC 和私有文件夹
- 探测
- 代码库
- 绑定重定向
- App.Config 和 Machine.config
- 强命名和弱命名
- 开发模式
目标读者
本文不是初级水平,具有配置文件的基本知识和程序集结构的读者也可以在第一次阅读中受益。
类型解析的探索
CLR – 公共语言运行时 – 负责定位和绑定引用程序集的过程。定位是找到硬盘上正确程序集的过程。绑定是将程序集加载到应用程序地址空间中的过程。
当 JIT 遇到需要解析的用户定义类型时,探索就开始了。然后 CLR 尝试检测类型定义在哪里
- 同一程序集中的同一文件
- 同一程序集中的不同文件
- 不同程序集
本文讨论第三个选项。
一般过程块
CLR 从一个阶段移动到另一个阶段,如上所述,以确定要加载的确切程序集。这种流程的原因是每个阶段都可能覆盖前一个阶段的信息。虽然这看起来像是一个冗余,但它确实是必要的,因为需要在安装后更改部署文件。例如,在安装服务包时,系统管理员希望保持以前的安装正常运行。这种需求要求更改新版本新程序集的定位和绑定过程。
1) 根据名称和版本搜索引用的程序集
- 配置文件 - App.config
CLR 在清单检查后检查 App.config。如果引用程序集版本被覆盖,则 App.config 设置具有优先权。
- 发布者策略文件
CLR 在 App.config 检查后检查发布者策略文件。发布者策略文件作为更新、热修复或服务包的一部分部署。当更新的共享/公共程序集具有新版本(与程序集清单不同)时,使用发布者策略文件。发布者策略文件中的设置具有优先权,除非 App.config 文件设置了安全模式(
<PUBLISHERPOLICY apply= "no">
)。 - 机器配置文件
CLR 在发布者策略文件检查后检查 machine.config 文件。该文件由机器上的所有 .NET 应用程序共享。如果版本不同,则 Machine.config 中的设置具有优先权。
2) 检查以前引用的程序集
CLR 检查程序集是否已加载(由于以前的代码执行语句)。如果找到,CLR 使用它。起初,这看起来像 Redmond 的狐狸有一个设计错误——为什么不在第一阶段检查以前加载的程序集列表中的程序集?原因是需要首先检查所需版本。
3) 在 GAC(全局程序集缓存)中检查
如果在第 2 阶段未找到且清单暗示程序集是强命名的,则 CLR 检查 GAC。如果存在,GAC 具有优先权。
4) Codebase 或探测
前面的阶段告诉 CLR 所需的程序集版本是什么。在此阶段,CLR 尝试查找并加载程序集。
- 代码库
如果在应用程序配置文件中定义了
Codebase
标签,则 CLR 只检查定义的 location。如果程序集不在给定的 URL 中,则探测过程终止。 - 探测
如果配置文件中没有
Codebase
标签,或者尝试从 URL 中检索文件失败,则 CLR 开始探测过程。 - 子目录
在应用程序目录中搜索,然后在其子目录中搜索程序集名称。
[application base] / [assembly name].dll [application base] / [assembly name] / [assembly name].dll
如果引用的程序集具有区域性定义,则 CLR 会在以下子目录中检查
[application base] / [culture] / [assembly name].dll [application base] / [culture] / [assembly name] / [assembly name].dll
CLR 还会检查 BinPath。
提示:CLR 一旦找到引用程序集(仅名称搜索)就会终止探测过程。如果程序集正确 - 一切正常 - 否则绑定失败(引发
Filenotfound
异常)。
功能和术语
静态和动态加载
在静态加载中,CLR 在程序集清单中检查程序集。静态引用程序集列表在构建过程中输入到文件中。在动态加载中,CLR 在运行时引入程序集。此功能封装在 System.Reflection
程序集中,它公开了 Assembly.Load
等方法(类似于 LoadLibrary
函数)。
私有和公共/共享程序集
共享程序集不部署在使用它们的应用程序的同一目录中。如果您这样做,CLR 不会抱怨,但将共享程序集复制到基本应用程序的某个目录并将其指向其他程序集是部署错误。通常,共享程序集在 GAC 或共享目录中注册(使用它们的应用程序需要知道这一点)。
强命名和弱命名程序集
两者之间的主要区别在于强命名程序集包含一个公共令牌。该令牌唯一标识程序集。GAC 中安装的共享程序集需要它。原因是某个开发人员创建的程序集与碰巧安装在同一台 PC 上的程序集具有相同的名称、区域性和版本,这种可能性很小。另一个区别(从前面暗示)是强命名程序集可以私有和公共部署(GAC),而弱命名程序集只能私有部署。
提示:不要将程序集复制到 GAC 文件夹 ([windows 主文件夹]\assembly)。程序集必须在 GAC 中注册。通过 .NET 命令提示符使用 GacUtil 或将程序集拖放到 GAC 窗口。
共享程序集和 GAC
复制到应用程序基本目录之外的共享程序集必须是强命名的,但不需要安装在 GAC 中。在这种情况下,必须在配置文件中使用 Codebase
标签指定共享程序集位置。应用程序套件可以创建一个共享文件夹并将共享程序集复制到其中。
以下是帮助决定何时使用 GAC 而不是专有共享文件夹的原因列表
- 第三方应用程序可能会使用共享程序集。将程序集复制到共享文件夹会强制使用它们的应用程序知道其位置。
- 当新版本程序集安装在 GAC 中时,并行执行更容易。它节省了处理不同版本的不同路径的时间。此外,使用专有的共享文件夹可能会导致 DLL Hell。
- 只有在 Windows 管理组中定义的用户才能在 GAC 中安装程序集(安全性)。
- 共享文件夹更容易出现鲁莽的用户错误,例如删除和覆盖。
- 节省磁盘存储:我知道,这是一个相当愚蠢的原因。如今,磁盘非常便宜,但您不能忽略反复将程序集复制到不同位置会产生一些存储影响的事实。
探测
这是定义程序集位置的两种方法之一。指令在配置文件中完成。您只能指定应用程序基本目录下的子目录(参见代码片段)。
CodeBase
这是定义程序集位置的两种方法之一。CLR 首先检查程序集版本,然后在配置文件中搜索覆盖 CodeBase
定义。版本属性仅对于强命名程序集是必需的。对于弱命名程序集,href
属性只能分配给子目录。此 URL 可以指向用户硬盘上的目录或 Web 地址。在 Web 地址的情况下,CLR 将自动下载文件并将其存储在用户的下载缓存中(<Documents and Settings>\<UserName>\Local Settings\Application Data\Assembly 下的一个子目录)。将来引用时,CLR 将从该目录加载程序集,而不是访问 URL。CodeBase
标签应仅在机器配置或发布者策略文件中定义,这些文件还重定向程序集版本(参见代码片段)。
绑定重定向
此功能使某个程序集能够绑定到不同的文件版本,并且对于服务包安装非常有用。通常重定向到 GAC 注册的程序集。GAC 允许我们安装具有不同版本的同一程序集。CLR 检查配置文件并相应地重定向绑定(参见代码片段)。
使用 DEVPATH 定位程序集
DEVPATH
是开发阶段的一个不错的功能。该功能通过延迟部署阶段的决策来简化开发生命周期。
开发人员可以创建一个 DEVPATH
环境变量,指向程序集的构建输出目录。按照以下步骤享受此功能
- 在机器配置文件中指定元素。确保将其添加到相关的框架版本。
CLR 在
DEVPATH
环境变量中描述的路径中搜索引用的程序集。 - 定义一个环境变量。名称:
DEVPATH
。值:path
。确保将其作为系统变量输入,并且路径值以 \ 结尾。
示例:在 </CONFIGURATION>
标签上方添加以下语句。
</configuration>
<runtime>
<developmentMode developerInstallation="true"/>
</runtime>
</configuration>
代码片段
以下代码片段与以下示例相关
- 应用程序名称是 HelloWorld。
- 应用程序位于 c:\MyTest。
- ProductCabinet 是 MyTest 目录下的一个子目录 (c:\MyTest\ProductCabinet)。
- 文件 ReferencedFile.dll 位于 ProductCabinet 目录下。
探测
<?xml version="1.0" encoding="utf?8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas?microsoft?com:asm.v1">
<probing privatePath=" ProductCabinet" />
</assemblyBinding>
</runtime>
</configuration>
代码库
<?xml version="1.0" encoding="utf?8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas?microsoft?com:asm.v1">
<dependentAssembly>
<codeBase version="1.0.0.0"
href= "file:///c:\ MyTest\ProductCabinet ReferencedFile.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
绑定重定向
<?xml version="1.0" encoding="utf?8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name=" ReferencedFile"
publicKeyToken="99ab3ba45e0b54a8"
culture="en-us" />
<bindingRedirect oldVersion="1.0.0.0"
newVersion="2.0.0.0"/>
</dependentAssembly>
<publisherPolicy apply="no">
</assemblyBinding>
</runtime>
</configuration>
工具
- Fuslogvw – 程序集绑定日志查看器
“程序集绑定日志查看器显示失败程序集绑定的详细信息。此信息可帮助您诊断 .NET Framework 在运行时无法定位程序集的原因。这些失败通常是由于程序集部署到错误的位置或版本号或区域性不匹配造成的。公共语言运行时无法定位程序集通常会在您的应用程序中显示为
TypeLoadException
”(MSDN)提示:确保将 HKLM\Software\Microsoft\Fusion\ForceLog 注册表值设置为 1(该值为
DWORD
)。 - NET Framework 配置工具
NET Framework 配置允许您配置程序集、远程处理服务和代码访问安全策略细节。(MSDN)
资源
结论
.NET 程序集的加载和绑定过程很复杂。然而,了解幕后发生的事情是一个重要且不可避免的主题。熟悉此过程是开始为部署做准备并回答一些重要问题的唯一好方法,例如
- 配置/覆盖选项有哪些?
- 要使用哪个正确的配置文件?
- 如何成功安装热修复和服务包?
- 如何解决
FileNotFound
异常?