在 .NET 应用程序中可以进行三种类型的引用。此答案仅涵盖以下列表中的前两个。
- 文件参考。
- 项目参考
- 服务参考。
必须解析每个引用。参考解析是以文件的形式定位参考的具体实例的过程。项目引用的解析方式与文件引用的解析方式相同。项目引用只允许您引用尚不存在的程序集(因为它是构建过程的输出。)
重要的是要理解引用解析在编译时和运行时都发生,并且两者的过程完全不同。不理解这一点会导致无休止的头痛。相信我,我知道。
运行时参考解析(又名绑定)
当一个应用程序被调用时,它必须被加载到内存中。如果应用程序使用另一个程序集中的对象,则该程序集也必须加载到内存中。 .NET 框架使用以下过程来执行此操作。
- 确定引用程序集的版本。
- 在编译时将引用程序集的版本写入应用程序清单。除非在配置中被覆盖,否则将使用此版本。
- 应用程序/web.config
- 发布策略(覆盖 application/web.config)
- machine.config(覆盖发布策略和 application/web.config)
- 如果程序集之前已加载,则从缓存中重新使用。
- 如果提供了强名称,请搜索 GAC。
- 探头
- 如果指定了代码库元素,则使用。
- 如果未找到则绑定失败。
- 如果版本、文化或公钥不匹配,则绑定失败。
- 搜索应用程序基路径。按简单名称匹配,如果第一个匹配是错误的版本,则失败。
- 如果未提供文化,则搜索根目录,然后搜索根目录/[程序集名称]
- 如果提供了文化,则搜索根/[文化],然后搜索根/[文化]/[程序集名称]。
- 如果 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 等)时,会有一个额外的编译步骤。这两种情况的参考解决方案如下所述。
MSBuild
程序集解析发生在 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 运行时编译器将尝试使用您指定的 exact 版本进行编译。如果在 MSBuild 编译阶段针对不同版本的程序集编译应用程序的非动态组件,这可能会导致问题。经常出现这种情况,因为 MSBuild 将使用它可以找到的最新版本,并且只有在您设置 specific version = true 时才会使用确切的版本。
其他资源:
http://jack.ukleja.com/diagnosing-asp-net-page-compilation-errors/
http://blog.fredrikhaglund.se/blog/2008/02/23/get-control-over-your-assembly-dependencies/
https://dhakshinamoorthy.wordpress.com/2011/10/01/msbuild-assembly-resolve-order/
http://www.beefycode.com/post/resolving-binary-references-in-msbuild.aspx