在一个库/ NuGet包

时间:2018-03-15 19:09:35

标签: c# .net msbuild nuget

我想要什么

我希望我的库能够使用各种版本的NuGet包,并在更改之间对API进行重大更改。我还没有进一步调查,但这条路看起来很有希望:

  • 通过指定extern namespace aliases
  • 来引用具有不同API的库的所有版本
  • 为所需的类创建代理,包含标志/例外/以及告诉实际支持的内容。
  • 根据实际加载到应用程序中的版本,在运行时选择正确的代理。
  • 依赖于不存在的API的代码不会被调用,所以一切都应该正常工作。

虽然这可能看起来很复杂,但它比在单独的程序集中支持每个版本的更直接的方法有很多好处:

  • 我的图书馆的版本不会像1.2.3-for-2.3.4-2.6.8一样变得混乱。在这种情况下,我甚至不知道版本控制是如何工作的。
  • NuGet用户不必在几个软件包之间进行选择,一个软件包适合所有软件包。
  • 升级版本很简单,不需要删除和添加我的软件包。

问题

然而,目前尚不清楚它是否可行。甚至在获得代理和检测当前版本之前,我就已经坚持了基础知识。

我甚至无法向我的.csproj添加多个PackageReference节点,只有一个参考实际上有效。有一个workaround for adding extern aliases直接由NuGet支持,但我无法达到这一点,因为我无法获得两个引用。如果我以某种方式得到两个,我就无法区分它们。

问题

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

    我正在使用CsConsoleFormat库来格式化控制台输出。我想直接支持流行的命令行软件包的所有相关版本,这样无论使用什么命令行解析库,都可以添加相当多的命令行帮助和类似的东西,几乎不需要编码。

    我想宣布"我只支持最新版本"在我的情况下有点可以接受,但即使它更复杂,我也会得到更广泛的支持。理想情况下,我想要一个NuGet包,它声明对最低支持版本的依赖,但支持最新版本的所有内容

    到目前为止的进展

    我有点工作,但有很多问题。有关详细信息,请参阅issue on GitHub NuGet Home

3 个答案:

答案 0 :(得分:4)

如果你坚持使用extern别名 - 你可以直接添加多个版本引用,如dll文件,而不是nuget包。

假设我想依赖于Newtonsoft.Json包10.0.3+版。 但是,如果用户安装了版本11,我想使用仅在此版本中可用的通用JsonConverter<T>类(11)。然后我的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);
        }
    }
}

和v11

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 intellisense有时不喜欢这种方法,并且在实际上一切都编译得很好时会显示智能感知错误。

  • 编译时可能会出现“版本冲突”警告。

答案 1 :(得分:3)

NuGet只解析单个软件包版本。

如果声明对最低支持版本的依赖性,则任何引用项目都可以将依赖项升级到较新版本。

只要依赖包的作者不引入重大更改,它应该可以找到。

即使您使用反射来查看所使用的实际程序集版本,您会发现许多程序包作者不会在发行版之间更改程序集版本。这是为了避免在经典.NET Framework项目中绑定重定向的需要,因为所有版本都相同,并且NuGet将根据使用项目的已解析包版本选择正确的DLL。同样,只要不存在重大变化,这就有好处。

您可以用来支持不同包的模式是提供许多消费者可以选择的“平台”包。然后,特定于平台的包将引用具有可共享逻辑的公共包。

然后,“平台”将是例如“MyLogic.XUnit”或“MyLogic.NUnit”(假设测试助手为例)引用“MyLogic.Common”

答案 2 :(得分:1)

这不是一个完整的答案,但我在你的GitHub issue page注意到你在项目中引用了.NET Standard和.NET Framework库。众所周知这是行不通的。

引用.NET标准团队的announcement

  

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

这可能是你遇到的。