.NET 中的引用是如何定位的?

How are references located in .NET?

.NET 在运行时使用什么过程来定位引用的程序集,它与在编译期间用于定位引用的程序集的过程是否不同?我对搜索的位置及其搜索顺序以及可能影响结果的任何 parameters/configuration 设置特别感兴趣。

.NET 应用程序中可以进行三种类型的引用。此答案仅涵盖以下列表中的前两个。

  1. 文件引用。
  2. 项目参考
  3. 服务参考。

必须解决每个引用。引用解析是以文件形式定位引用的具体实例的过程。项目引用的解析方式与文件引用的解析方式相同。项目引用只允许您引用尚不存在的程序集(因为它是构建过程的输出。)

重要的是要了解引用解析发生在编译时和运行时,并且两者的过程完全不同。不了解这一点可能会导致无休止的头痛。相信我,我知道。

运行时引用解析(又名绑定)

调用应用程序时,必须将其加载到内存中。如果应用程序使用另一个程序集中的对象,则该程序集也必须加载到内存中。 .NET 框架使用以下过程来执行此操作。

  1. 确定引用程序集的版本。
    • 引用程序集的版本在编译时写入应用程序清单。除非在配置中被覆盖,否则将使用此版本。
      • application/web.config
      • 发布策略(覆盖 application/web.config)
      • machine.config(覆盖发布策略和 application/web.config)
  2. 如果程序集之前已加载,则从缓存中重新使用。
  3. 如果提供强名称,搜索 GAC。
  4. 探测
    • 如果指定了代码库元素,则使用。
      • 未找到绑定失败
      • 如果版本、文化或 public 密钥不匹配,则绑定失败。
    • 搜索应用程序基本路径。按简单名称匹配,如果第一个匹配是错误的版本则失败。
      • 如果未提供区域性,则搜索根,然后搜索根/[程序集名称]
      • 如果提供了文化,则搜索 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 等)在首次访问时会发生额外的编译步骤。下面介绍这两种情况的参考解析。

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>

这里发生了很多事情,我并不声称完全理解,但我会尽量指出重要的部分。

  1. 查找参考的最常见位置是(按搜索顺序)
    • 手动添加到项目的文件(例如 /lib/coollib.dll>
    • 提示路径指定的位置。
    • 广汽
    • 应用程序输出路径。
  2. 标记为 Copy Local = true 的引用在 编译后被复制到应用程序输出路径。这意味着此设置的值对 MSBuild 的引用解析过程没有影响。请注意,复制本地 UI 设置映射到项目文件中的 <private> 元素。
  3. MSBuild 将始终尝试使用给定程序集可用的最新版本,除非指定了特定版本 = true。此设置的默认值为 false,这意味着在搜索 GAC 时,将始终使用最新版本的 DLL,而不管项目定义中指定的版本。

ASP.NET 运行时编译器

除非之前在构建时使用预编译选项编译到项目输出文件夹中,否则所有动态内容(aspx、asc、asax、cshtml 等)都会在首次​​访问应用程序时在运行时编译一次。此动态内容还可以依赖于其他程序集。 system.web > compilation > assemblies 元素用于告知 ASP.NET 运行时编译器这些依赖项,以便它可以引用它们。

ASP.NET 运行时编译器将搜索以下位置以查找这些引用。

  1. 应用程序专用程序集缓存(又名 PAC),即 /bin 文件夹。
  2. GAC(如果使用强名称指定引用)。

请注意,默认情况下,根 web.config 使用通配符语法引用一些系统程序集和 PAC 中的所有程序集。这意味着您很少需要手动显式添加对 system.web > compilation > assemblies 元素的引用。在许多情况下,您可以而且应该完全删除该元素。它应该只包含对存储在 GAC 中的程序集的引用。使用 Copy Local = true 是包含 ASP.NET 运行时编译器所需的非 GAC 引用的推荐方法。

另请注意,如果您使用 system.web > compilation > assemblys 元素使用程序集的强名称指定特定版本号,则可能会出现许多细微错误。 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-dependencies/ https://dhakshinamoorthy.wordpress.com/2011/10/01/msbuild-assembly-resolve-order/ http://www.beefycode.com/post/resolving-binary-references-in-msbuild.aspx