以 .Net 框架为目标时消除程序集绑定重定向

Eliminating assembly binding redirects when targeting the .Net framework

我们有一个包含 100 多个项目的解决方案(丑陋的大型 .Net Framework Monolith 应用程序的一部分)。现在我们正在努力将尽可能多的代码作为 NuGet 包移动到我们的内部存储库。但现在我们陷入了依赖地狱,因为即使我们所有的代码都未签名,但大多数第 3 方 NuGet 都已签名。以及所有 Microsoft 软件包。

自从我们从 packages.config 迁移到 PackageReference 后,情况变得更糟了,因为许多依赖项变得隐式(可传递)。一方面我们想使用 PackageReference,因为:

  1. 在可能的情况下,向 SDK 风格的项目迈进了一步。
  2. 准确地说,因为它显示了我们使用的内容,而不会因可传递的依赖而使项目混乱。
  3. 这是未来,对吗?

但另一方面,对所有这些绑定重定向进行分类是很糟糕的。最糟糕的是 - 从 msbuild 到 VS IDE 不一致,参见 Why does console build generate radically different project.assets.json than that generated with VS IDE build?

我想一劳永逸地摆脱它们。我的想法是:

  1. 禁止与绑定重定向相关的所有警告 - MSB3277 和 MSB3247
  2. 从所有配置文件中删除它们
  3. 在运行时使用专用代码解析程序集

不知道有没有人试过这种方法。不可能是我们是唯一在与绑定重定向作斗争的人,这种折磨手段无疑是因为编程 .Net 而不是 Java.

的罪过。

我有一个具体的问题 - 有没有人成功地用运行时的逻辑替换所有配置时间程序集绑定重定向?我想抑制所有与绑定重定向相关的警告,并在留在 .Net Framework(非核心)领域时一劳永逸地忘记它们。

编辑 1

因此,希望看到我们拥有的具体绑定重定向警告示例。好的:

MSB3247: Found conflicts between different versions of the same dependent assembly. In Visual Studio, double-click this warning (or select it and press Enter) to fix the conflicts; otherwise, add the following binding redirects to the "runtime" node in the application configuration file: <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Buffers" culture="neutral" publicKeyToken="cc7b13ffcd2ddd51" /><bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /></dependentAssembly></assemblyBinding><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Diagnostics.DiagnosticSource" culture="neutral" publicKeyToken="cc7b13ffcd2ddd51" /><bindingRedirect oldVersion="0.0.0.0-4.0.3.1" newVersion="4.0.3.1" /></dependentAssembly></assemblyBinding><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Memory" culture="neutral" publicKeyToken="cc7b13ffcd2ddd51" /><bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" /></dependentAssembly></assemblyBinding><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" /><bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" /></dependentAssembly></assemblyBinding> [C:\xyz\tip\Services\Platform\WBDataSvc\TestServices\TestServices.csproj]
MSB3247: Found conflicts between different versions of the same dependent assembly. In Visual Studio, double-click this warning (or select it and press Enter) to fix the conflicts; otherwise, add the following binding redirects to the "runtime" node in the application configuration file: <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Buffers" culture="neutral" publicKeyToken="cc7b13ffcd2ddd51" /><bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /></dependentAssembly></assemblyBinding><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Memory" culture="neutral" publicKeyToken="cc7b13ffcd2ddd51" /><bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" /></dependentAssembly></assemblyBinding><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" /><bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" /></dependentAssembly></assemblyBinding> [C:\xyz\tip\Services\Platform\DeviceServices\DeviceServices.csproj]
MSB3247: Found conflicts between different versions of the same dependent assembly. In Visual Studio, double-click this warning (or select it and press Enter) to fix the conflicts; otherwise, add the following binding redirects to the "runtime" node in the application configuration file: <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Buffers" culture="neutral" publicKeyToken="cc7b13ffcd2ddd51" /><bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /></dependentAssembly></assemblyBinding><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Memory" culture="neutral" publicKeyToken="cc7b13ffcd2ddd51" /><bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" /></dependentAssembly></assemblyBinding><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" /><bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" /></dependentAssembly></assemblyBinding> [C:\xyz\tip\Services\Platform\WBDataSvc\DataSvc\DataSvc.csproj]
MSB3247: Found conflicts between different versions of the same dependent assembly. In Visual Studio, double-click this warning (or select it and press Enter) to fix the conflicts; otherwise, add the following binding redirects to the "runtime" node in the application configuration file: <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Buffers" culture="neutral" publicKeyToken="cc7b13ffcd2ddd51" /><bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /></dependentAssembly></assemblyBinding><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Memory" culture="neutral" publicKeyToken="cc7b13ffcd2ddd51" /><bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" /></dependentAssembly></assemblyBinding><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" /><bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" /></dependentAssembly></assemblyBinding> [C:\xyz\tip\Services\Platform\WBDataSvc\MobileWebService\MobileWebService.csproj]
MSB3277: Found conflicts between different versions of "System.Diagnostics.DiagnosticSource" that could not be resolved.  These reference conflicts are listed in the build log when log verbosity is set to detailed. [C:\xyz\tip\ServerJobs\BackgroundJobTests\BackgroundJobTests.csproj]
MSB3277: Found conflicts between different versions of "System.Diagnostics.DiagnosticSource" that could not be resolved.  These reference conflicts are listed in the build log when log verbosity is set to detailed. [C:\xyz\tip\ServerJobs\ImportJobsTests\ImportJobsTests.csproj]
MSB3277: Found conflicts between different versions of "System.Diagnostics.DiagnosticSource" that could not be resolved.  These reference conflicts are listed in the build log when log verbosity is set to detailed. [C:\xyz\tip\Services\Platform\WBDataSvc\PayrollEngineDALTests\Payroll.Engine.DB.Tests.csproj]
MSB3277: Found conflicts between different versions of "System.Diagnostics.DiagnosticSource" that could not be resolved.  These reference conflicts are listed in the build log when log verbosity is set to detailed. [C:\xyz\tip\Services\Platform\WBDataSvc\PayrollEngineTests\Payroll.Engine.Tests.csproj]
MSB3277: Found conflicts between different versions of "System.Diagnostics.DiagnosticSource" that could not be resolved.  These reference conflicts are listed in the build log when log verbosity is set to detailed. [C:\xyz\tip\Services\Platform\WBDataSvc\ReportingEngineSupportTests\ReportingEngineSupportTests.csproj]
MSB3277: Found conflicts between different versions of "System.Diagnostics.DiagnosticSource" that could not be resolved.  These reference conflicts are listed in the build log when log verbosity is set to detailed. [C:\xyz\tip\Services\Platform\WBDataSvc\RuleEngineTests\RuleEngineTests.csproj]
MSB3277: Found conflicts between different versions of "System.Diagnostics.DiagnosticSource" that could not be resolved.  These reference conflicts are listed in the build log when log verbosity is set to detailed. [C:\xyz\tip\Services\Platform\WBDataSvc\WbDbTests\WbDbTests.csproj]
MSB3277: Found conflicts between different versions of "System.Diagnostics.DiagnosticSource" that could not be resolved.  These reference conflicts are listed in the build log when log verbosity is set to detailed. [C:\xyz\tip\Test\UnitTests\UnitTests.csproj]

现在果然,我们可以将缺少的绑定重定向混入配置文件中,但是:

  1. 鉴于 Why does console build generate radically different project.assets.json than that generated with VS IDE build? 这还不够好,因为 VS IDE 在 project.assets.json 中产生了一组不同的依赖关系(我知道,完全是假的。一个问题在 DC 中是打开的)并且我们已经看到开发人员在 VS IDE 与命令行
  2. 中构建需要不同的重定向集
  3. 绑定重定向单调增长 - 我们被告知何时添加,但不知道何时删除。因此,随着时间的推移,我们有无数不同的绑定重定向,它们需要维护,因为有些已经过时,有些不再需要了。

真是一大麻烦。

我决定不尝试用运行时程序集解析替换配置时间绑定重定向。原因 - 我不知道如何可靠地给出:

  • 不同类型的可运行项目 - 控制台应用程序、Asp.Net 应用程序、WCF 服务、单元测试项目。我们都有。
  • 代码可能会生成不同的应用程序域,并且每个应用程序域都必须具有此程序集解析逻辑。我认为一般情况下根本不可能。

相反,我决定利用我们设置的以下方面:

  • 我们已经对我们引用的所有 NuGet 包实施了一致的版本控制。迁移到 PackageReference 大大减少了我们可以控制的包的数量——因此出现了许多问题。但是我们直接引用的那些是有序的。
  • 我们现在有 project.assets.json 个文件,这些文件展示了 NuGet 包和项目引用的全貌。我们无法更改传递依赖项(就像我们可以使用 packages.config),但我们可以知道所有这些依赖项。

这使得编写一个工具可以读取 project.assets.json 给定项目(并递归地读取它所依赖的所有其他项目)并基于它们做两件事:

  1. 识别不同版本中提到的所有 NuGet 包依赖项。例如。如果 NuGet 包 X 依赖于 NuGet 包 Y v1,但 NuGet 包 Z 依赖于 Y v2,则 Y 有问题。并且我们可以识别这个条件并确定最高版本的文件路径 - v2.
  2. 自动更新绑定重定向。
  3. 构建完成后,将第一步中确定的文件复制到发布的目录中。

这样 bin 文件夹中的二进制文件将不依赖于解决方案中项目的构建顺序,我们将有一个确定的过程来维护绑定重定向。

这项工作正在进行中,但看起来很有希望 - https://github.com/MarkKharitonov/GenerateDotNetBindingRedirects