在一个库/ NuGet包中支持多个版本的NuGet包

14

我想要的

我希望我的库能够与多个具有API更改的NuGet包版本一起运行。虽然我还没有深入研究,但这条路看起来很有前途:

  • 通过指定 extern 命名空间别名 引用所有具有不同 API 的库版本。
  • 为所需类创建代理,使用标志/异常/其他方式告诉实际支持什么。
  • 根据实际加载到应用程序中的版本选择正确的代理。
  • 不会调用不存在 API 的代码,因此一切都应该正常工作。

虽然这可能看起来很复杂,但它比直接支持每个版本的单独程序集具有许多优点:

  • 我的库的版本不会像 1.2.3-for-2.3.4-to-2.6.8 那样变得混乱。在这种情况下,我甚至不知道版本控制应该如何工作。
  • NuGet 用户不必在几个包之间做出选择,一个包适合所有人。
  • 升级版本将变得简单明了,无需删除和添加我的包。

问题

但是,目前尚不清楚是否可能。甚至在进行代理和检测当前版本之前,我就卡在了基础知识上。

我甚至无法将多个 PackageReference 节点添加到我的 .csproj 文件中,只有一个引用实际起作用。虽然有一种 添加 extern 别名的解决方法,但 NuGet 不直接支持它们,但我无法达到那个点,因为我无法获得两个引用。如果我以某种方式获得了两个引用,我也无法区分它们。

问题

  1. 可以使用 extern 命名空间别名和代理这种方式来实现对多个版本的支持吗?
  2. 如果是,如何添加对多个版本的 NuGet 包的引用并在代码中使用它们?
  3. 如果不是,那么正确的方法是什么?
  4. 背景

    我正在开发 CsConsoleFormat 库,用于格式化控制台输出。我想直接支持所有相关版本的流行命令行包,以便可以通过几乎没有编码的方式添加漂亮的命令行帮助和其他内容,无论使用哪个命令行解析库。

    我想在我的情况下声明“我只支持最新版本”可能有些可接受,但我更愿意有更广泛的支持,即使这更加复杂。理想情况下,我希望 NuGet 包声明依赖于最低支持版本,但支持到最新版本

    目前进展

    我有点让它工作了,但出现了很多问题。请参见 GitHub NuGet Home 上的问题获取更多详情。


您是否需要将您的软件包设置为单个 DLL?我的意思是,NuGet 软件包可能包含多个 DLL,在安装时它们都将被目标项目引用。 - Evk
@Evk 目前基于 SDK 的项目还不支持此功能(https://github.com/NuGet/Home/issues/3891)。如果我采用这种方法,我的源代码会变得更加混乱,因为我必须将所有特定版本的代码精确地放到单独的程序集中,而不能像 extern aliases 那样依赖于非内联方法。消费者项目的输出也会变得混乱,可能会有许多不需要运行的库。总的来说,即使官方支持此功能,我认为这种方法仍然比 extern aliases 差。 - Athari
有一些解决方法是有效的(我亲自尝试过),但其余部分是正确的 - 在安装您的软件包时,消费者将引用所有这些dll。 - Evk
您可以通过直接引用dll(通过Reference而不是包引用)来实现所需功能。将所有版本(除了最小版本)作为ReferenceSpecificVersionAliases包含,然后将最小版本作为PackageReference包含。结果将会有您想要的所有版本别名(最小版本将在全局命名空间中无别名可用)。打包的NuGet只会依赖>=最小版本,其他dll(其他版本)不会包含在包中。我刚试过了,它就是这样工作的(当然会有“版本之间的冲突”警告)。 - Evk
3个回答

14
如果您坚持使用外部别名-您可以直接添加多个版本引用,作为dll文件,而不是nuget包。
假设我想依赖于Newtonsoft.Json软件包的版本10.0.3+。 然而,如果用户安装了版本11-我想使用仅在此版本(11)中可用的通用JsonConverter<T>类。那么我的csproj可能看起来像这样:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <Version>1.0.4</Version>
  </PropertyGroup>
  <ItemGroup>
    <!-- Nuget reference -->
    <!-- Only this one will be included as dependency to the packed nuget -->
    <PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
  </ItemGroup>
  <ItemGroup>
    <!-- Direct reference to the specific version -->
    <Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
      <!-- Path to v11 dll -->
      <HintPath>Newtonsoft.Json.v11.dll</HintPath>
      <Aliases>js11</Aliases>
      <SpecificVersion>true</SpecificVersion>
    </Reference>    
  </ItemGroup>
</Project>

然后我有代理接口:
public interface ISerializer {
    string Serialize<T>(T obj);
}

还有两个实现版本:v10(使用全局非别名命名空间):

using System;
using global::Newtonsoft.Json;

namespace NugetRefMain {
    internal class Js10Serializer : ISerializer
    {
        public string Serialize<T>(T obj)
        {
            Console.WriteLine(typeof(JsonConvert));
            return JsonConvert.SerializeObject(obj);
        }
    }
}

版本11

extern alias js11;
using System;
using js11::Newtonsoft.Json;

namespace NugetRefMain {
    internal class Js11Serializer : ISerializer {
        public string Serialize<T>(T obj) {
            // using JsonConverter<T>, only available in v11
            Console.WriteLine(typeof(JsonConverter<T>));
            return JsonConvert.SerializeObject(obj);
        }
    }
}

最后是工厂,根据当前可用的json.net版本创建序列化程序:
public static class Serializers {
    public static ISerializer Create() {
        var version = typeof(JsonConvert).Assembly.GetName().Version;
        if (version.Major == 10)
            return new Js10Serializer();
        return new Js11Serializer();
    }
}

现在,如果我将它打包成Nuget - 它将仅依赖于Newtonsoft.Json版本10.0.3,而且就这些了。然而,如果用户安装了版本11的Newtonsoft.Json,它将使用此版本可用的功能。
缺点: - Visual Studio / Resharper智能感知有时不喜欢这种方法,并显示智能感知错误,但实际上一切都编译得很好。 - 在编译时可能会出现“版本冲突”警告。

3
NuGet只解析单个软件包版本。
如果您声明对最低支持版本的依赖关系,则任何引用项目都可以将依赖项升级到新版本。
只要依赖包的作者不引入破坏性更改,它就应该正常工作。
即使您使用反射查看实际使用的程序集版本,您会发现许多软件包作者在发布之间不更改程序集版本。这是为了避免在经典.NET Framework项目中需要绑定重定向,因为所有版本都相同,NuGet将根据消费项目的已解析软件包版本选择正确的DLL。同样,只要没有破坏性更改,这对于我们来说是很好的。
一个您可以使用以支持不同软件包的模式是提供许多“平台”软件包,消费者可以从中选择。特定于平台的软件包将引用具有可共享逻辑的公共软件包。
然后,“平台”可能是例如“MyLogic.XUnit”或“MyLogic.NUnit”(假设测试助手作为示例)引用“MyLogic.Common”。

问题在于我希望你的示例中的"MyLogic.NUnit"支持各种版本的NUnit,因此需要多个包,如"MyLogic.NUnit-1"、"MyLogic.NUnit-2"、"MyLogic.NUnit-2.2",这使得维护和使用这些包都很痛苦。 - Athari

-1

这不是一个完整的答案,但我注意到在您的GitHub issue page上,您在项目中引用了.NET Standard和.NET Framework库。已知这样做是无法正常工作的。

引用.NET标准团队的announcement

.. 另一个症状是在构建时有关程序集版本的警告 ..

这可能是您遇到的问题。


1
可以作为评论而不是答案。 - ViktorMS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接