.NET 在运行时使用什么过程来定位被引用的程序集,它与在编译期间用于定位被引用程序集的过程有什么不同?我对搜索的位置及其搜索顺序以及可能影响结果的任何参数/配置设置特别感兴趣。
1 回答
在 .NET 应用程序中可以进行三种类型的引用。此答案仅涵盖以下列表中的前两个。
- 文件参考。
- 项目参考
- 服务参考。
每个引用都必须解决。参考解析是以文件的形式定位参考的具体实例的过程。项目引用的解析方式与文件引用的解析方式相同。项目引用只允许您引用尚不存在的程序集(因为它是构建过程的输出。)
重要的是要了解引用解析发生在编译时和运行时,并且每个过程都完全不同。不理解这一点会导致无休止的头痛。相信我,我知道。
运行时参考解析(又名绑定)
当一个应用程序被调用时,它必须被加载到内存中。如果应用程序使用另一个程序集中的对象,则该程序集也必须加载到内存中。.NET 框架使用以下过程来执行此操作。
- 确定引用程序集的版本。
- 引用程序集的版本在编译时写入应用程序清单。除非在配置中被覆盖,否则将使用此版本。
- 应用程序/web.config
- 发布策略(覆盖 application/web.config)
- machine.config(覆盖发布策略和 application/web.config)
- 引用程序集的版本在编译时写入应用程序清单。除非在配置中被覆盖,否则将使用此版本。
- 如果以前加载了程序集,则从缓存中重新使用。
- 如果提供了强名称,请搜索 GAC。
- 探测
- 如果指定了代码库元素,则使用。
- 如果没有找到绑定失败。
- 如果版本、文化或公钥不匹配,则绑定失败。
- 搜索应用程序基本路径。按简单名称匹配,如果第一个匹配是错误的版本,则失败。
- 如果没有提供文化,搜索根然后根/[程序集名称]
- 如果提供了文化,则搜索 root/[culture],然后搜索 root/[culture]/[assembly name]。
- 如果 web/app.config 指定探测元素,则在 privatePath 中搜索路径。路径必须相对于应用程序根目录。
- 如果指定了代码库元素,则使用。
有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/yx7xezcf%28v=vs.110%29.aspx。
编译时参考分辨率
编译时间解析在生成过程中发生在 MSBuild 中。MSBuild 是 Visual Studio 和 TFS 使用的构建引擎。请注意,对于 ASP.NET 应用程序,动态组件(aspx、asc、asax、cshtml 等)在首次访问时会发生额外的编译步骤。这两种情况的参考解决方案如下所述。
微软构建
程序集解析发生在 ResolveAssemblyReferences MSBuild 目标中。此目标调用 ResolveAssemblyReference 任务,将 AssemblySearchPaths 的值传递给 SearchPaths 参数,该参数分配的值如下。
<PropertyGroup>
<!--
The SearchPaths property is set to find assemblies in the following order:
(1) Files from current project - indicated by {CandidateAssemblyFiles}
(2) $(ReferencePath) - the reference path property, which comes from the .USER file.
(3) The hintpath from the referenced item itself, indicated by {HintPathFromItem}.
(4) The directory of MSBuild's "target" runtime from GetFrameworkPath.
The "target" runtime folder is the folder of the runtime that MSBuild is a part of.
(5) Registered assembly folders, indicated by {Registry:*,*,*}
(6) Legacy registered assembly folders, indicated by {AssemblyFolders}
(7) Resolve to the GAC.
(8) Treat the reference's Include as if it were a real file name.
(9) Look in the application's output folder (like bin\debug)
-->
<AssemblySearchPaths Condition=" '$(AssemblySearchPaths)' == ''">
{CandidateAssemblyFiles};
$(ReferencePath);
{HintPathFromItem};
{TargetFrameworkDirectory};
{Registry:$(FrameworkRegistryBase),$(TargetFrameworkVersion),$(AssemblyFoldersSuffix)$(AssemblyFoldersExConditions)};
{AssemblyFolders};
{GAC};
{RawFileName};
$(OutDir)
</AssemblySearchPaths>
这里发生了很多事情,我并不声称了解所有内容,但我会尝试指出重要的部分。
- 查找参考的最常见位置是(按搜索顺序)
- 手动添加到项目的文件(例如 /lib/coollib.dll>
- 提示路径指定的位置。
- 广汽
- 应用程序输出路径。
- 用 Copy Local = true 标记的引用在编译后被复制到应用程序输出路径。这意味着此设置的值对 MSBuild 的引用解析过程没有影响。请注意,复制本地 UI 设置映射到
<private>
项目文件中的元素。 - MSBuild 将始终尝试使用给定程序集可用的最新版本,除非指定了特定版本 = true。此设置的默认值为 false,这意味着在搜索 GAC 时,将始终使用最新版本的 DLL,而不管项目定义中指定的版本如何。
ASP.NET 运行时编译器
除非之前在构建时使用预编译选项编译到项目输出文件夹中,否则所有动态内容(aspx、asc、asax、cshtml 等)都会在首次访问应用程序时在运行时编译一次。此动态内容还可以依赖于其他程序集。system.web > 编译 > 程序集元素用于告诉 ASP.NET 运行时编译器这些依赖关系,以便它可以引用它们。
ASP.NET 运行时编译器将搜索以下位置以查找这些引用。
- 应用程序私有程序集缓存(又名 PAC),即 /bin 文件夹。
- GAC(如果使用强名称指定引用)。
请注意,默认情况下,根 web.config 使用通配符语法引用一些系统程序集和 PAC 中的所有程序集。这意味着您很少需要手动显式添加对 system.web > 编译 > 程序集元素的引用。在许多情况下,您可以并且应该完全删除该元素。它应该只包含对存储在 GAC 中的程序集的引用。使用 Copy Local = true 是包含 ASP.NET 运行时编译器所需的非 GAC 引用的推荐方法。
另请注意,如果您使用 system.web > 编译 > 程序集元素使用程序集的强名称指定特定版本号,则可能会出现许多细微错误。ASP.NET 运行时编译器将尝试使用您指定的确切版本进行编译。如果在 MSBuild 编译阶段针对不同版本的程序集编译应用程序的非动态组件,这可能会导致问题。经常出现这种情况,因为 MSBuild 将使用它可以找到的最新版本,并且只有在您设置特定版本 = true 时才会使用确切版本。
其他资源:
http://jack.ukleja.com/diagnosing-asp-net-page-compilation-errors/ http://blog.fredrikhaglund.se/blog/2008/02/23/get-control-over-your-assembly-依赖项/ https://dhakshinamoorthy.wordpress.com/2011/10/01/msbuild-assembly-resolve-order/ http://www.beefycode.com/post/resolving-binary-references-in-msbuild.aspx