web-dev-qa-db-fra.com

Prise en charge de plusieurs versions du paquet NuGet dans une bibliothèque/paquet NuGet

Ce que je veux

Je souhaite que ma bibliothèque fonctionne avec une gamme de versions d'un package NuGet avec des modifications radicales de l'API entre les modifications. Je n'ai pas étudié plus avant, mais ce chemin semble prometteur:

  • Référencez toutes les versions de la bibliothèque avec différentes API en spécifiant alias d'espace de nom externe .
  • Créez des mandataires pour les classes nécessaires, avec des drapeaux/exceptions/n'importe quoi pour indiquer ce qui est réellement supporté.
  • Choisissez le bon proxy au moment de l'exécution en fonction de la version effectivement chargée dans l'application.
  • Le code qui dépend d'API inexistante ne sera pas appelé, donc tout devrait bien fonctionner.

Bien que cela puisse paraître compliqué, cela présente de nombreux avantages par rapport à une approche plus simple consistant à prendre en charge chaque version dans une assemblée distincte:

  • La version de ma bibliothèque ne deviendra pas un gâchis comme 1.2.3-pour-2.3.4-à-2.6.8. Je ne sais même pas comment le contrôle de version est censé fonctionner dans ce cas.
  • Les utilisateurs de NuGet n'auront pas à choisir entre plusieurs packages, un seul package convient à tous.
  • Mettre à jour des versions serait simple, il ne sera pas nécessaire de supprimer et d’ajouter mon paquet.

Problème

Cependant, il n'est pas clair si c'est possible du tout. Même avant de passer aux mandataires et de détecter la version actuelle, je suis coincé avec les bases.

Je ne peux même pas ajouter plusieurs nœuds PackageReference à mon .csproj, une seule référence fonctionne réellement. Il existe un solution de contournement pour l'ajout d'alias externes } qui ne sont pas directement pris en charge par NuGet, mais je ne peux pas en arriver là, car je ne peux pas obtenir deux références. Et si j'en ai deux, je ne pourrai pas les distinguer.

Des questions

  1. La prise en charge de plusieurs versions peut-elle être implémentée de cette manière, en utilisant des alias et des mandataires d'espace de noms externe?
  2. Si oui, comment ajouter des références à plusieurs versions d'un paquet NuGet et les utiliser dans le code?
  3. Si non, quelle est la bonne approche alors?

Contexte

Je travaille sur la bibliothèque CsConsoleFormat pour formater la sortie de la console. Je souhaite prendre en charge directement toutes les versions pertinentes des packages de ligne de commande les plus utilisés, afin de pouvoir ajouter de jolies aides de ligne de commande et ce genre de choses, pratiquement sans codage, quelle que soit la bibliothèque utilisée pour l'analyse de ligne de commande.

J'imagine que déclarer "je ne supporte que la dernière version" est un peu acceptable dans mon cas, mais je préférerais un support plus large même si c'est plus compliqué. Idéalement, je veux un paquet NuGet qui déclare sa dépendance à la version la plus basse supportée, mais supporte tout jusqu'à la dernière version .

Progrès jusqu'à présent

Je l'ai un peu fait fonctionner, mais avec de nombreux problèmes. Voir numéro sur GitHub NuGet Home } pour plus de détails.

8
Athari

Si vous insistez sur les alias externes - vous pouvez ajouter plusieurs références de version directement, sous forme de fichier dll, et non sous forme de package Nuget.

Supposons que je veuille créer une dépendance vis-à-vis du paquet Newtonsoft.Json version 10.0.3 +. Toutefois, si l’utilisateur dispose de la version 11, je souhaite utiliser la classe générique JsonConverter<T> disponible uniquement dans cette version (11). Ensuite, mon csproj pourrait ressembler à ceci:

<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>

Ensuite, j'ai une interface proxy:

public interface ISerializer {
    string Serialize<T>(T obj);
}

Et deux implémentations, v10 (utilise un espace de noms global sans alias):

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);
        }
    }
}

Et 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);
        }
    }
}

Et enfin l’usine qui crée un sérialiseur en fonction de la version actuelle de 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();
    }
}

Maintenant, si je mets ceci comme nuget - il aura une dépendance unique sur Newtonsoft.Json version 10.0.3 et c'est tout. Toutefois, si l'utilisateur installe Newtonsoft.Json de la version 11, il utilisera les fonctionnalités disponibles dans cette version.

Désavantages:

  • Visual Studio\Resharper intellisense n'aime pas cette approche parfois et montre des erreurs intellisense alors qu'en réalité, tout compile parfaitement.

  • Vous pourriez avoir des avertissements de "conflit de version" lors de la compilation.

4
Evk

NuGet ne résout que les versions à un seul paquet.

Si vous déclarez une dépendance à la version minimale prise en charge, tout projet de référencement peut mettre à niveau la dépendance vers une version plus récente.

Tant que les auteurs du paquet dépendant n'introduisent pas de changements radicaux, cela devrait fonctionner.

Même si vous utilisez la réflexion pour examiner les versions actuelles de Assembly utilisées, vous constaterez que de nombreux auteurs de packages ne modifient pas la version de Assembly entre les versions. Cela évite d'avoir à rediriger les liens dans les projets .NET Framework classiques, car toutes les versions sont identiques et NuGet sélectionne le bon DLL en fonction de la version du package utilisé qui a été résolue. Encore une fois, c'est bon tant qu'il n'y a pas de changements radicaux.

Un modèle que vous pouvez utiliser pour prendre en charge différents packages consiste à fournir de nombreux packages "plate-forme" parmi lesquels le consommateur peut choisir. Le paquet spécifique à la plate-forme ferait alors référence à un paquet commun avec une logique partageable.

La "plateforme" serait alors par exemple "MyLogic.XUnit" ou "MyLogic.NUnit" (en supposant que les aides de test soient un exemple) faisant référence à "MyLogic.Common"

3
Martin Ullrich

Ce n'est pas une réponse complète, mais j'ai remarqué sur votre page de problème GitHub que vous faites référence à la fois aux bibliothèques .NET Standard et .NET Framework de votre projet. Ceci est connu pour ne pas fonctionner correctement.

Citation de annonce de l'équipe .NET Standard ,

.. Un autre symptôme est les avertissements au moment de la construction concernant les versions d'Assembly ..

qui peut être ce que vous rencontrez.

1
NGambit