Sélectionner des assemblys référencés par les projets

Les assemblys sont utilisés de deux façons différentes lors d’une génération. La première consiste à compiler, ce qui permet au code du consommateur de package de compiler sur des API dans l’assembly. Cela permet également à IntelliSense de donner des suggestions. La seconde est le runtime, où l’assembly est copié dans le répertoire bin et est utilisé pendant l’exécution du programme. Certains auteurs de package souhaiteraient que seuls leurs propres assemblys (ou un de leur sous-ensemble d’assemblys) soient disponibles pour leurs consommateurs de package au moment de la compilation. Ils ont besoin de fournir toutes leurs dépendances pour leur runtime. Ce document examine les façons d’atteindre ce résultat.

Nous vous recommandons d’avoir un seul package par assembly et des dépendances de package pour les autres assemblys. Lorsque NuGet restaure un projet, il effectue une sélection de ressources et prend en charge l’inclusion, l’exclusion et la création de différentes classes de ressources privées. Pour empêcher les dépendances de package de devenir des ressources de temps de compilation pour toute personne utilisant votre package, vous pouvez rendre compile les ressources privées. Dans le package généré, cela entraîne l’exclusion de compile de la dépendance. Notez que les ressources privées par défaut lorsqu’aucune ressource n’est fournie sont contentfiles;build;analyzers. Par conséquent, vous devez utiliser PrivateAssets="compile;contentfiles;build;analyzers" dans votre PackageReference ou ProjectReference.

<ItemGroup>
  <ProjectReference Include="..\OtherProject\OtherProject.csproj" PrivateAssets="compile;contentfiles;build;analyzers" />
  <PackageReference Include="SomePackage" Version="1.2.3" PrivateAssets="compile;contentfiles;build;analyzers" />
</ItemGroup>

Si vous créez un package à partir d’un fichier personnalisé nuspec, plutôt que de laisser NuGet générer automatiquement un package pour vous, votre nuspec doit utiliser l’attribut XML exclude.

<dependencies>
  <group targetFramework=".NETFramework4.8">
    <dependency id="OtherProject" version="3.2.1" exclude="Compile,Build,Analyzers" />
    <dependency id="SomePackage" version="1.2.3" exclude="Compile,Build,Analyzers" />
  </group>
</dependencies>

Il existe trois raisons pour lesquelles il s’agit de la solution recommandée.

Tout d’abord, les assemblys utiles sont souvent référencés par de nouveaux assemblys/packages. Aujourd’hui un assembly d’utilitaire peut être destiné à n’être utilisé que par un seul package. Cela peut rendre tentant le fait d’expédier les deux assemblys dans un seul et même package. Si un second package veut utiliser l’assembly d’utilitaire « privé » dans le futur, il faudra soit le déplacer dans un nouveau package et déclarer l’ancien package comme dépendance du premier, ou expédier le package d’utilitaire à la fois dans le package existant et dans le nouveau package. Si l’assembly est expédié dans deux packages différents et qu’un projet référence les deux packages et s’il existe différentes versions de l’assembly utilitaire dans les deux packages, NuGet ne peut pas aider à gérer les versions.

Deuxièmement, il peut arriver que les développeurs qui utilisent votre package souhaitent également utiliser les API de vos dépendances. Par exemple, prenons le package Microsoft.ServiceHub.Client version 3.0.3078. Si vous téléchargez le package et inspectez le fichier nuspec, vous pouvez voir qu’il répertorie deux packages commençant Microsoft.VisualStudio. considérés comme dépendances. Cela signifie qu’il en a besoin au moment du runtime, mais qu’il exclut également leurs ressources de compilation. Cela signifie que les projets utilisant Microsoft.ServiceHub.Client n’ont pas les API Visual Studio disponibles dans IntelliSense ou si elles génèrent le projet, sauf si le projet installe explicitement ces packages. Et c’est l’avantage que les dépendances de package avec exclusion de ressource ont. Les projets utilisant votre package, s’ils souhaitent également utiliser vos dépendances, peuvent également ajouter une référence au package pour rendre les API disponibles pour eux-mêmes.

Enfin, par le passé, certains auteurs de packages ont été confus par la sélection d’assemblys NuGet pour les packages prenant en charge plusieurs version cible de .Net Framework lorsque leur package contient également plusieurs assemblys. Si votre assembly principal prend en charge différents versions cibles de .Net Framework pour votre assembly utilitaire, le répertoire lib/ dans lequel tous vos assemblys peut ne pas être évident. En séparant chaque package par nom d’assembly, déterminer dans quel dossier lib/ devrait aller chaque assembly devient plus intuitif. Notez que cela ne signifie pas avoir des packages Package1.net48 et Package1.net6.0. Cela signifie avoir lib/net48/Package1.dll et lib/net6.0/Package6.0 dans Package1, et lib/netstandard2.0/Package2.dll et lib/net5.0/Package2.dll dans Package2. Lorsque Nuget restaure un projet, Nuget effectue indépendamment la sélection des ressources pour les deux packages.

Notez également que les ressources d’inclusion/exclusion de dépendance sont utilisées uniquement par les projets utilisant PackageReference. Tout projet d’installation de votre package utilisant packages.config installe les dépendances et dispose également des API de celles-ci. packages.config est uniquement pris en charge par les anciens modèles de projet Visual Studio .NET Framework . Les projets de style SDK, même ceux ciblant .NET Framework, ne prennent pas en charge packages.config. Ils prennent donc en charge les ressources d’inclusion/exclusion de dépendances.

PackageReference et packages.config disposent de différentes fonctionnalités disponibles. Si vous souhaitiez ou non prendre en charge les consommateurs de votre package qui utilisent PackageReference, packages.config ou les deux, modifie la façon dont vous devez signer votre package.

La cible MSBuild Pack de NuGet ne prend pas en charge automatiquement l’inclusion de références de projet dans le package. Elle répertorie uniquement ces projets référencés en tant que dépendances de package. Il existe un ticket sur GitHub, où les membres de la communauté ont partagé les moyens dont ils ont obtenu ce résultat. Cela implique généralement l’utilisation des métadonnées d’élément MSBuild PackagePath pour placer des fichiers n’importe où dans le package, comme décrit dans la documentation sur l’inclusion de contenu dans un package et en utilisant SuppressDependenciesWhenPacking pour éviter que les références de projet deviennent des dépendances de package. Il existe également des outils développés par la communauté qui peuvent être utilisés comme alternative au pack officiel de NuGet et qui prennent en charge cette fonctionnalité.

Prise en charge de PackageReference

Lorsqu’un consommateur de package utilise PackageReference, NuGet sélectionne les ressources de compilation et de runtime indépendamment, comme décrit précédemment.

Les ressources de compilation préfèrent ref/<tfm>/*.dll (par exemple ref/net6.0/*.dll), mais si cela n’existe pas, elle revient à lib/<tfm>/*.dll (par exemple lib/net6.0/*.dll).

Les ressources de runtime préfèrent runtimes/<rid>/lib/<tfm>/*.dll (par exemple (runtimes/win11-x64/lib/net6.0/*.dll)), mais si cela n’existe pas, elle revient à lib/<tfm>/*.dll.

Du fait que les assemblys dans ref\<tfm>\ ne sont pas utilisés au moment de l’exécution, vous pouvez utiliser des assemblys de métadonnées uniquement afin de réduire la taille du package.

Prise en charge de packages.config

Les projets qui utilisent packages.config pour gérer les packages NuGet ajoutent normalement des références à tous les assemblys listés dans le répertoire lib\<tfm>\. Le répertoire ref\ ayant été ajouté pour prendre en charge PackageReference, il est ignoré quand packages.config est utilisé. Pour définir explicitement quels assemblys sont référencés dans les projets utilisant packages.config, le package doit utiliser l’élément <references> dans le fichier nuspec. Par exemple :

<references>
    <group targetFramework="net45">
        <reference file="MyLibrary.dll" />
    </group>
</references>

Les cibles du pack MSBuild ne prennent pas en charge l’élément <references>. Consultez la documentation sur l’empaquetage à l’aide d’un fichier .nuspec lors de l’utilisation du pack MSBuild.

Remarque

Le projet packages.config utilise un processus appelé ResolveAssemblyReference pour copier les assemblys dans le répertoire de sortie bin\<configuration>\. L’assembly du projet est copié, après quoi le système de build recherche les assemblys référencés dans le manifeste de l’assembly, puis copie ces assemblys, et répète cette séquence de manière récursive pour tous les assemblys. Cela signifie que si l’un des assemblys chargés uniquement par réflexion (Assembly.Load, MEF ou une autre framework d’injection de dépendances), il peut ne pas être copié dans le répertoire de sortie bin\<configuration>\ de votre projet bien qu’il soit dans bin\<tfm>\. Cela signifie également que cela fonctionne uniquement pour les assemblys .NET, et non pour le code natif appelé avec P/Invoke.

Prenant en charge PackageReference et packages.config

Important

Si un package contient l’élément nuspec <references> et ne contient pas d’assemblys dans ref\<tfm>\, NuGet publie les assemblys listés dans l’élément nuspec <references> à la fois comme ressources de compilation et ressources d’exécution. Cela signifie qu’il y aura des exceptions de runtime si les assemblys référencés doivent charger un autre assembly dans le répertoire lib\<tfm>\. Par conséquent, il est important d’utiliser à la fois le nuspec <references> pour la prise en charge de packages.config, ainsi que la duplication d’assemblys dans le dossier ref/ pour la prise en charge de PackageReference. Le dossier du package runtimes/ n’a pas besoin d’être utilisé, il a été ajouté à la section ci-dessus pour qu’elle soit la plus exhaustive possible.

Exemple

Mon package doit contenir trois assemblys, MyLib.dll, MyHelpers.dll et MyUtilities.dll, qui ciblent .NET Framework 4.7.2. MyUtilities.dll contient des classes destinées à être utilisées uniquement par les deux autres assemblys. Je ne veux donc pas que ces classes soient disponibles dans IntelliSense ou au moment de la compilation pour les projets qui utilisent mon package. Mon fichier nuspec doit contenir les éléments XML suivants :

<references>
    <group targetFramework="net472">
        <reference file="MyLib.dll" />
        <reference file="MyHelpers.dll" />
    </group>
</references>

J’ai besoin de vérifier que mon contenu de package est le suivant :

lib\net472\MyLib.dll
lib\net472\MyHelpers.dll
lib\net472\MyUtilities.dll
ref\net472\MyLib.dll
ref\net472\MyHelpers.dll