Comment cibler plusieurs fois une bibliothèque de classes .NET Core avec csproj?

94

Lorsque .NET Core utilisait encore le project.jsonformat, vous pouviez créer une bibliothèque de classes ciblant plusieurs frameworks (par exemple net451, netcoreapp1.0).

Maintenant que le format de projet officiel csprojutilise MSBuild, comment spécifier plusieurs frameworks à cibler? Je suis en train de chercher ce billet depuis les paramètres du projet dans VS2017, mais je suis en mesure de cibler seulement un cadre unique à partir des cadres de base .NET (il ne liste même pas les autres versions complètes .NET Framework que je ne l' ai installé) :

entrez la description de l'image ici

Gigi
la source
Ce n'est pas ce à quoi il est censé ressembler, vous devriez obtenir les choix .NETStandard 1.x répertoriés dans la liste déroulante. On ne sait pas très bien comment cela s'est passé, assurez-vous de choisir le bon modèle de projet pour commencer. Doit être «Bibliothèque de classes (.NET Standard)». Il semble que vous ayez choisi le modèle d'application console, puis que vous ayez commencé à modifier les propriétés, pas dans le bon sens. Si vous avez en fait utilisé le modèle de bibliothèque de classes, l'installation ne s'est pas bien déroulée.
Hans Passant
J'ai en fait sélectionné Bibliothèque de classes (.NET Core).
Gigi
2
Bien, donc ce n'est pas le bon si vous voulez multi-cibler. Vous devez choisir un .NETStandard pour rendre la bibliothèque utilisable sur plusieurs plates-formes.
Hans Passant
Cela l'éclaircit. Vous pouvez rédiger une réponse à partir de vos commentaires si vous le souhaitez.
Gigi

Réponses:

119

Vous devez modifier manuellement le fichier de projet et ajouter des s au TargetFramework par défaut et le changer essentiellement en TargetFrameworks . Ensuite, vous mentionnez le Moniker avec un ; séparateur.

Vous pouvez également placer les références de package Nuget dans un ItemGroup conditionnel manuellement ou en utilisant VS Nuget Package Manager.

Voici à quoi devrait ressembler votre .csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netstandard1.6;net452</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup Condition="'$(TargetFramework)' == 'net452'">
    <PackageReference Include="Microsoft.Azure.DocumentDB">
      <Version>1.12.0</Version>
    </PackageReference>
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.6'">
    <PackageReference Include="Microsoft.Azure.DocumentDB.Core">
    <Version>1.1.0</Version>
    </PackageReference>
  </ItemGroup>
</Project>

Une autre solution de contournement que je fais ces jours-ci en raison d'une documentation manquante est que je crée un projet dans VS2015 et forme le project.json en utilisant la documentation disponible et intellisense, puis j'ouvre la solution dans VS2017 et j'utilise la mise à niveau intégrée. Je vais ensuite regarder le fichier csproj pour comprendre comment réaliser cette configuration.

Multi-ciblage de cibles plus ésotériques sans Moniker :

Microsoft:

Les PCL ne sont pas recommandés +

Bien que les PCL soient pris en charge, les auteurs de packages doivent prendre en charge netstandard à la place. La norme de plate-forme .NET est une évolution des PCL et représente la portabilité binaire entre les plates-formes en utilisant un seul surnom qui n'est pas lié à une statique comme les monikers portable-a + b + c.

Si vous souhaitez cibler un profil portable , il n'a pas de prédéfini moniker donc les profils portables aussi ne peut donc conclure TargetFrameworkIdentifier, TargetFrameworkVersionet TargetFrameworkProfile. De plus, une constante de compilateur n'est pas définie automatiquement. Enfin, vous devez ajouter toutes les références d'assemblage, aucune n'est fournie par défaut.

Cet exemple ci-dessous est tiré d'un projet qui a utilisé le dynamicmot - clé, donc il avait en plus besoin de l' Microsoft.CSharpassemblage, vous pouvez ainsi voir comment il est référencé pour différentes cibles.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netstandard1.5;net40;portable40-net45+sl5+win8+wp8</TargetFrameworks>
  </PropertyGroup>

  <PropertyGroup Condition="'$(TargetFramework)'=='portable40-net45+sl5+win8+wp8'">
    <TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier>
    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
    <TargetFrameworkProfile>Profile158</TargetFrameworkProfile>
    <DefineConstants>$(DefineConstants);PORTABLE158</DefineConstants>
  </PropertyGroup>

  <ItemGroup Condition="'$(TargetFramework)'=='netstandard1.5'">
    <PackageReference Include="Microsoft.CSharp" Version="4.3.0" />
    <PackageReference Include="System.ComponentModel" Version="4.3.0" />
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)'=='net40'">
    <Reference Include="Microsoft.CSharp" />
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)'=='portable40-net45+sl5+win8+wp8'">
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Windows" />
  </ItemGroup>
</Project>
Un Boo
la source
1
Devez-vous faire cela en éditant le csproj manuellement, ou cela peut-il être fait via VS?
Gigi
1
Le ciblage multiple @Gigi doit être effectué manuellement. Les packages Nuget peuvent être créés via VS2017 ou manuellement.
Aboo
2
J'avais le même problème, et tout fonctionnait bien après avoir ajouté le s à <TargetFramework>. Je souhaite vraiment que ces choses soient mieux documentées.
Negorath
2
@AsadSaeeduddin Les chances sont que Resharper vous montre les gribouillis et non Visual Studio. Le $ (TargetFramework) est juste utilisé pour rendre un ItemGroup disponible pour un framework / Moniker particulier.
Aboo
2
@ORMapper si le package NuGet est construit correctement, cela devrait se produire automatiquement lors de l'importation. Cela signifie que si NuGetPackageA prend déjà en charge plusieurs frameworks, vous n'avez pas besoin de le placer dans un groupe d'éléments conditionnés. Maintenant, si vous avez besoin de référencer PackageA pour .net framework et PackageB pour .net core, cela doit être placé dans un groupe d'éléments conditionnés. Il n'y a pas d'option dans l'interface à partir d'aujourd'hui (octobre 2017).
Aboo
24

Vous pouvez modifier manuellement le .csprojfichier pour cela et définir TargetFrameworks(non TargetFramework) la propriété.

<TargetFrameworks>net451;netstandard1.4</TargetFrameworks>

Par exemple, voir EFCore.csproj: https://github.com/aspnet/EntityFrameworkCore/blob/951e4826a38ad5499b9b3ec6645e47c825fa842a/src/EFCore/EFCore.csproj

Marcheur de nuit
la source
9
Merci! Ça me tue. Dans «Les éléments du style de programmation» de Brian Kernigan écrit il y a quatre décennies, il parle des erreurs d'utilisation de variables qui diffèrent d'une seule lettre à la fin. Cela aurait été beaucoup plus clair si le nom était "TargetFrameworkList".
howardlo
L'exemple sur lequel vous pointez n'inclut pas de propriété <TargetFrameworks>.
RenniePet
@RenniePet, merci! Le fichier de projet a changé dans le temps. J'ai changé le lien en un commit concret, où il y a <TargetFrameworks>.
Marcheur de nuit
12

J'ai en fait sélectionné Bibliothèque de classes (.NET Core).

Ce n'est pas le modèle de projet souhaité si votre bibliothèque doit fonctionner sur plusieurs plates-formes cibles. Avec ce modèle de projet, votre bibliothèque ne peut être utilisée que dans un projet qui cible .NETCore. L'approche de la bibliothèque PCL a été retirée, vous devez maintenant choisir un .NETStandard.

Pour ce faire, démarrez le projet avec le modèle de projet «Bibliothèque de classes (.NET Standard)». Vous avez maintenant la possibilité de choisir la version .NETStandard. La grille de compatibilité actuelle est ici .

J'espère qu'ils garderont cet article lié à jour. Ceci est en évolution, .NETStandard 2.0 a été cloué mais n'est pas encore disponible. Ciblé pour le deuxième trimestre de 2017, fin du printemps probablement, il indique actuellement que 97% ont été réalisés. J'ai entendu les concepteurs dire que l'utilisation de 1.5 ou 1.6 n'est pas recommandée, pas assez compatible avec 2.0

Hans Passant
la source
Comment cela fonctionne-t-il si vous avez différentes dépendances pour différents frameworks cibles? Je veux dire que project.jsonvous pouvez spécifier des dépendances spécifiques pour un framework cible.
Gigi
1
Je suis sûr à 90% que vous devez oublier que cela a jamais existé. C'était un désordre déroutant qui n'était qu'une mesure provisoire pour amorcer .NETCore. Utilisez la grille de compatibilité à laquelle j'ai lié.
Hans Passant
Je m'en doutais. Merci beaucoup pour cette clarification!
Gigi
4
@HansPassant multi-target est toujours la meilleure option si vous avez du code hérité et en même temps votre développement greenfield peut être effectué dans l'une des versions récentes du framework complet ou dotnetcore.
Stefano Ricciardi
1
Même le greenfield n'est pas encore nécessairement compatible .NET Core. J'utilise Azure Function Apps, mais leur version .NET Core ne prend en charge que quelques déclencheurs. (J'ai besoin de déclencheurs Service Bus, donc je suis coincé avec .NET Framework, et une très grande bibliothèque de fonctionnalités métier peut devenir multi-cible.) La plate-forme de Microsoft est actuellement en désordre. C'est l'équivalent moderne de DLL Hell.
McGuireV10
5

J'ai fait un guide du débutant sur le framework de réseau multi-ciblage et netcore qui commence par le correctif simple en 1 ligne, puis vous guide à travers chacune des complications.

L'approche la plus simple consiste à faire fonctionner en premier une cible netcore ou netstandard. Modifiez ensuite le fichier csproj et suivez ces étapes pour les autres cibles.

  1. Découvrez les sections conditionnelles de votre fichier csproj, afin de pouvoir déclarer différentes dépendances pour chaque cible. Créez des sections conditionnelles pour chaque cible.
  2. Ajoutez <Reference />spour System. * Les dll pour toutes les cibles de réseau en lisant simplement ce que les messages d'erreur de construction disent qu'il manque.
  3. Traitez les dépendances NuGet <PackageReference />sdans les cas où elles ne sont pas les mêmes pour chaque cible. L'astuce la plus simple est de revenir temporairement au ciblage unique afin que l'interface graphique gère correctement les références Nuget pour vous.
  4. Traitez du code qui ne se compile pas sur toutes les cibles, en apprenant une variété créative de techniques, de solutions de contournement et de gains de temps.
  5. Sachez quand réduire vos pertes lorsque le coût de l'ajout de cibles supplémentaires est trop élevé.
Chris F Carroll
la source