如果 msbuild 的版本低于项目依赖项之一的要求,则 msbuild 拒绝复制未签名的 dll - 这是设计使然吗?

msbuild refuses to copy unsigned dll if it is of lower version than demanded by one of the project dependencies - is it by design?

我有以下情况:

  1. 所有涉及的dll都是未签名的
  2. 解决方案中的所有项目都依赖于 Shared.dll
  3. 的 1.0.21221.1 版本
  4. 解决方案中部分项目的部分NuGe​​t依赖依赖同一个dll的1.0.21237.1版本
  5. 构建 Web 应用程序时(让我们将其命名为 Api),预计会将 Shared.dll 从 $(OutDir) 复制到$(OutDir)_PublishedWebsites\Api\bin 文件夹。在 $(OutDir) 中找到的 Shared.dll 的版本为 1.0.21221.1.
  6. 未复制 Shared.dll,Web 应用程序无法 运行。

这是二进制日志的证据:

图表 A - 版本冲突:

图表 B - ResolveAssemblyReference 指示不要复制 Shared.dll:

我知道 msbuild 不喜欢版本冲突的想法,但不复制 dll 会产生彻头彻尾的错误,因为应用程序无法启动。

我知道可以通过添加程序集绑定重定向来解决它。但是我认为对于未签名的程序集来说是不必要的。我是理解错了还是漏掉了什么?

编辑 1

以下是我对评论中提出的问题的回答:

(不幸的是我被要求混淆一些关键字,我不知道为什么)

  1. Api项目引用Shared.dll到底是怎么回事?

正如我们在 图表 B 中看到的那样 Shared.dll 是 Api 的传递依赖。事实上,Api 依赖于 Xyz.BusinessApi 是这样的:

<Reference Include="Xyz.BusinessAPI" />

现在 DLL 通过相应的 NuGet 依赖项依赖于 Shared.dll,这里是 Xyz.BusinessAPI 的 project.assets.json 文件的片段:

  1. 还有哪些其他项目参考 Shared.dll 以及如何参考?

有很多项目将其作为版本 1.0.21221.1 的 NuGet 包引用。问题是一些项目还引用了另外两个 NuGet 包,而这两个 NuGet 包又依赖于同一个 NuGet 包的版本 1.0.21237.1。这在 RAR 输出中指示 - 请参阅 图表 A

我想强调 - 没有项目引用 Shared.dll 作为原始 dll,仅作为 NuGet 包或通过其他 NuGet 或项目或 项目 dll 间接引用。 Project dll 是来自先前构建的解决方案的项目的 dll - 我们不允许项目引用其他解决方案,因此如果项目是在先前的解决方案中构建的,那么它将在后续解决方案中作为 DLL 被引用。

  1. 从OutDir复制到_PublishedWebsites\api\bin的机制是什么?

这是来自 C:\Program Files (x86)\Microsoft Visual Studio19\Enterprise\MSBuild\Microsoft\VisualStudio\v16.0\WebApplications\Microsoft.WebApplication.targets 的标准 Web 应用程序发布目标 _CopyFilesMarkedCopyLocal:

    <!-- copy any referenced assemblies to _PublishedWebsites\app\bin folder -->
    <Copy SourceFiles="@(ReferenceCopyLocalPaths)"
          DestinationFiles="@(ReferenceCopyLocalPaths->'$(WebProjectOutputDir)\bin\%(DestinationSubDirectory)%(Filename)%(Extension)')"
          SkipUnchangedFiles="true"
          Retries="$(CopyRetryCount)"
          RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/>
  1. Shared.dll 是如何进入 OutDir 的?

我们所有的代码都内置在共享 bin 目录中 - 我们将 OutDir 设置为所有项目的相同值。因此,所有项目二进制文件及其依赖项,包括 Shared.dll 首先在那里结束。

  1. binlog是否有双写?

是的,但我认为它们不相关:

简短的回答是您的项目间接依赖于 Shared.dll 的两个版本:1.0.21221.11.0.21237.1

RAR(MSBuild ResolveAssemblyReference 任务)检查了所有 .dll 的所有引用并找到了这两个版本。它报告了冲突:

注意Shared.dll21221在方括号中报告找到的文件路径,21237报告[](表示没有找到该版本的文件)。

使用 There was a conflict under($rar)$warning under($rar) 搜索这些内容很有用。

现在,OutDir 只包含 Shared.dll 21221,因此无法在任何地方找到版本为 21237 的 Shared.dll。

诀窍是搜索 Shared.dll under($rar project(api.csproj)) 您会从 RAR 中找到相关消息:

Considered "C:\Xyz\bin.link\Shared.dll",
        but its name "Shared, Version=1.0.21221.1, Culture=neutral, PublicKeyToken=null"
        didn't match the expected name "Shared, Version=1.0.21237.1, Culture=neutral, PublicKeyToken=null".

所以,它看到了冲突,决定统一到更高版本(21237),但没有找到那个版本的文件。所以 21221 的引用不是 CopyLocal,因为它没有在那个版本上统一,而对 21237 的引用没有被解析,因为找不到那个版本的文件。

要解决此问题,我建议添加对版本 21237 的 Shared.dll 的显式引用(通过 NuGet 或通过包引用上的 GeneratePathProperty 元数据:https://docs.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#generatepathproperty)。如果您使用 GeneratePathProperty,则可以使用 $(PkgFoo_Bar)\lib\net472\Shared.dll 或类似方法直接引用 .dll。还要添加从 21221 到 21237 的绑定重定向以解决冲突。一旦正确的版本 (21237) 出现在您的 OutDir 中,它将被正确复制到输出。

希望这能说明问题。