c# - PowerShell:为什么我能够在一个 session 中加载同一 .NET 程序集的多个版本以及 "bypass Dependency hell"?

标签 c# .net powershell .net-4.0 powershell-5.1

我在 Windows 10 x64、PowerShell 5.1 版中工作。

我有多个版本的 .NET Framework 4.0 程序集 (.dll)(由我自己)用 C# 编写。程序集未签名。它们的版本通过 [assembly: AssemblyVersion("X.X.X.X")]AssemblyInfo.cs 中设置。 [assembly: AssemblyFileVersion("X.X.X.X")] 标记在我的 AssemblyInfo.cs 中不存在!这些程序集在常规 .NET Framework 应用程序中使用,它们不是专门的 PowerShell 模块,也没有 list 文件。

我在一些 PowerShell 脚本中使用这个程序集来创建对象并从中调用方法。

我有这个程序集的两个版本,比如说 1.1.1.1001.2.1.100。当我通过 Import-Module "D:\path\to_v1.1\MyAssembly.dll" 在 PowerShell 中导入 1.1.1.100 版本时,它工作得很好。当我调用 Get-Module 时,我在列表中看到了这个导入的程序集:

ModuleType Version    Name
---------- -------    ----
Binary     1.1.1.100  MyAssembly

现在事情开始变得有趣了。我导入了另一个版本的程序集 Import-Module "D:\another\path\to_v1.2\MyAssembly.dll"。它再次工作得很好。所以这是我的问题:

Q1. 为什么没有显示错误?我希望得到一个错误:我正在尝试加载一个名称相同但版本不同的程序集。我认为您无法在一个上下文中加载同一程序集的两个不同版本。如何处理这些情况,使用哪些加载上下文?也许第二个版本根本没有加载?

Q2. Get-Module 现在显示具有相同名称和版本的两个 模块,如下所示:

ModuleType Version    Name
---------- -------    ----
Binary     1.1.1.100  MyAssembly
Binary     1.1.1.100  MyAssembly

我先导入哪个版本并不重要。在第二个 Import-Module 上,最早导入的版本Get-Module 列表中重复。我也只能使用最早导入的程序集版本中的类型/方法。

Q3. 我的程序集对我的其他程序集(在同一文件夹中)有一些依赖性。这些依赖项会自动解析并隐式“导入”到当前 session (我可以毫无问题地使用它们的类型)。主要 MyAssembly.dll 的每个版本都依赖于这些辅助程序集的不同版本。当我导入另一个版本的 MyAssembly 时,我也没有收到有关版本冲突的错误。再一次,我只能使用最早导入的二级程序集的类型。我读过有关“依赖 hell ”的文章,这应该是不可能的——那怎么可能呢?

我对 NuGet 包 Microsoft.CodeAnalysis.CSharp.dll、版本 3.4.03.11.0 进行了相同的实验.结果是一样的:版本及其依赖导入没有问题,但只有最早导入的版本可用。

总结

当我导入相同程序集的不同版本时,没有出现任何错误,但只有最早导入的版本可用。 我想了解为什么我能够在一个 session 中无错误地加载一个程序集的多个版本,PowerShell 通常如何处理这些情况以及为什么它显示两个具有相同版本的模块,为什么我不这样做'得到依赖 hell 。

我想了解发生了什么,而不仅仅是“让它工作”。

我在这里错过了什么?

谢谢!

最佳答案

在您的场景中,您的模块是独立的 .NET 程序集,它们实现了 PowerShell cmdlet。

在 Windows PowerShell 和 PowerShell (Core) 7+ 中,所有程序集都加载到相同:

    Windows PowerShell 中的
  • 应用程序域,它基于 .NET Framework

  • PowerShell (Core) 7+ 中的 ALC(程序集加载上下文),它基于 .NET Core/.NET 5+。 p>

默认情况下,支持加载同一个程序集的多个版本,加载到 session 中的第一个将获胜。

随后尝试加载程序集的不同版本:

  • 在 Windows PowerShell 中被悄悄忽略 - 无论您是否使用 Import-ModuleAdd-Type - LiteralPath 或其底层 .NET API 方法,[System.Reflection.Assembly]::LoadFrom() (一定要将完整文件路径传递给后者,因为 PowerShell 的工作目录通常与 .NET 不同;该方法输出一个表示程序集的对象,表面上是刚刚加载的,但它实际上表示 < em>最初加载的版本)。

  • 在 Powershell (Core) 7+ 中导致语句终止错误

    Assembly with same name is already loaded.
    

注意:您可以加载不同版本的程序集,即通过[System.Reflection.Assembly]::LoadFile()方法(注意 File 而不是 From),但是您可以使用其类型的唯一方法是通过反射


据我所知:

  • 没有cmdlet 实现程序集的解决方案,如您的情况。

    • cmdlet-implementing 我的意思是模块的 cmdlet 本身作为存储在程序集中的 .NET 类实现,而不是在 PowerShell 代码中实现的 cmdlet , 在 *.psm1 脚本模块文件中。

    • 无论您在 session 中首先导入/加载 cmdlet 实现程序集的哪个版本,在 session 的其余部分都是唯一可用的;程序集一旦加载,就无法卸载。

    • 另一种表达方式:虽然您可以在磁盘上拥有此类模块的并排版本,但您不能同时将它们加载到 session 中,据我所知 - 这只适用于基于脚本的模块(见下一点)。

  • 将多个版本的 helper 程序集加载到同一 session 中有一些非常重要的解决方案;这些在 Resolving PowerShell module assembly dependency conflicts 中有详细说明,其中还提供了广泛的背景信息。

    • 也就是说,如果您的 cmdlet 在 PowerShell 代码中实现并且该代码仅使用辅助 .NET 程序集,则链接文档提供了加载这些程序集版本的解决方案默认情况下会与其他模块加载的不同版本或同一模块的不同版本发生冲突。

关于c# - PowerShell:为什么我能够在一个 session 中加载同一 .NET 程序集的多个版本以及 "bypass Dependency hell"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68972925/

相关文章:

c# - 如何在c#中foreach以下xml

asp.net - 处置 DbContext 不处置 ObjectContext 实体

c# - 无法在WPF中单击单选按钮生成动态文本框

powershell - PowerShell 中的全局变量和局部变量

azure - 为什么我的 Azure PowerShell 任务脚本可以使用 find-module 找到模块,但不能使用 import-module?

Powershell,拒绝对网络资源的远程脚本访问

c# - 如何在 .NET 3.5 中再次使 StringBuilder 为空?

c# - 使用 C# 驱动程序从 mongo 集合中获取 DateTime

c# - HandleBars .Net 比较

.net - 来自扩展方法的 ArgumentNullException 或 NullReferenceException?