W jaki sposób można wykorzystać wiele celów dla biblioteki klas .NET Core za pomocą csproj?

96

Gdy .NET Core nadal używa tego project.jsonformatu, można zbudować bibliotekę klas dla wielu platform (np. Net451, netcoreapp1.0).

Teraz, gdy oficjalny format projektu csprojkorzysta z programu MSBuild, jak określić wiele platform docelowych? Staram się spojrzeć na to od ustawień projektu w VS2017, ale jestem w stanie kierować tylko pojedynczą ramy z ram .NET rdzenia (nawet nie notować inne pełne wersje .NET Framework które nie zostały zainstalowane) :

wprowadź opis obrazu tutaj

Gigi
źródło
To nie tak powinno wyglądać, powinieneś uzyskać opcje .NETStandard 1.x wymienione w menu rozwijanym. Nie jest jasne, jak to się stało, aby rozpocząć, wybierz odpowiedni szablon projektu. Powinna mieć wartość „Biblioteka klas (.NET Standard)”. Wygląda na to, że wybrałeś szablon aplikacji konsolowej, a następnie zacząłeś zmieniać właściwości, a nie we właściwy sposób. Jeśli faktycznie korzystałeś z szablonu Class Library, instalacja nie przebiegła pomyślnie.
Hans Passant
Właściwie wybrałem bibliotekę klas (.NET Core).
Gigi
2
Racja, więc to jest niewłaściwe, jeśli chcesz mieć wiele celów. Musisz wybrać standard .NETStandard, aby biblioteka mogła być używana na więcej niż jednej platformie.
Hans Passant
To wszystko wyjaśnia. Jeśli chcesz, możesz napisać odpowiedź na podstawie swoich komentarzy.
Gigi

Odpowiedzi:

122

Musisz ręcznie edytować plik projektu i dodać s do domyślnego TargetFramework i zasadniczo zmienić go na TargetFrameworks . Następnie wspominasz o Monikerze z ; separator.

Możesz również umieścić odwołania do pakietów Nuget w warunkowej ItemGroup ręcznie lub za pomocą Menedżera pakietów VS Nuget.

Oto jak powinien wyglądać plik .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>

Innym obejściem, które robię w dzisiejszych czasach z powodu brakującej dokumentacji, jest utworzenie projektu w VS2015 i utworzenie pliku project.json przy użyciu dostępnej dokumentacji i funkcji Intellisense, a następnie otwarcie rozwiązania w VS2017 i skorzystanie z wbudowanej aktualizacji. Następnie przyjrzę się plikowi csproj, aby dowiedzieć się, jak wykonać tę konfigurację.

Wielokrotne kierowanie na bardziej ezoteryczne cele bez monikera :

Microsoft:

PCL nie są zalecane +

Chociaż PCL są obsługiwane, autorzy pakietów powinni zamiast tego obsługiwać netstandard. Platforma .NET Platform Standard to ewolucja języków PCL i reprezentuje przenośność plików binarnych między platformami przy użyciu pojedynczego monikera, który nie jest powiązany ze statycznym, takim jak monikery portable-a + b + c.

Jeśli chcesz kierować Portable profilu nie ma predefiniowany przydomek też nie można wywnioskować więc przenośne profili TargetFrameworkIdentifier, TargetFrameworkVersionoraz TargetFrameworkProfile. Również stała kompilatora nie jest definiowana automatycznie. Na koniec musisz dodać wszystkie odwołania do zestawów, żadne nie są domyślnie dostarczane.

Poniższy przykład pochodzi z projektu, w którym użyto dynamicsłowa kluczowego, więc dodatkowo potrzebował Microsoft.CSharpzestawu, dzięki czemu można zobaczyć, jak to odwołania do różnych celów.

<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>
Aboo
źródło
1
Czy musisz to zrobić, edytując csproj ręcznie, czy można to zrobić za pomocą VS?
Gigi
1
@Gigi multi targetowanie należy wykonać ręcznie. Pakiety NuGet można wykonać za pośrednictwem VS2017 lub ręcznie.
Aboo
2
Miałem ten sam problem i wszystko działało dobrze po dodaniu s do <TargetFramework>. Naprawdę chciałbym, żeby te rzeczy były lepiej udokumentowane.
Negorath
2
@AsadSaeeduddin są szanse, że Resharper pokazuje zawijasy, a nie Visual Studio. $ (TargetFramework) służy po prostu do udostępniania ItemGroup dla określonego frameworka / Monikera.
Aboo
2
@ORMapper, jeśli pakiet NuGet jest poprawnie skonstruowany, powinno to nastąpić automatycznie podczas importowania. Oznacza to, że jeśli NuGetPackageA obsługuje już wiele struktur, nie musisz umieszczać go w warunkowej grupie elementów. Teraz, jeśli potrzebujesz odwołać się do PackageA dla .net framework i PackageB dla .net core, musisz to umieścić w warunkowej grupie elementów. Na dzień dzisiejszy (październik 2017 r.) W interfejsie nie ma opcji.
Aboo
24

Możesz ręcznie edytować .csprojplik dla tego i ustawić TargetFrameworks(nie TargetFramework) właściwość.

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

Na przykład patrz EFCore.csproj: https://github.com/aspnet/EntityFrameworkCore/blob/951e4826a38ad5499b9b3ec6645e47c825fa842a/src/EFCore/EFCore.csproj

Nocny spacerowicz
źródło
9
Dzięki! To mnie zabija. W „The Elements of Programming Style” Briana Kernigana, napisanym cztery dekady temu, mówi o błędach stosowania zmiennych różniących się na końcu jedną literą. Byłoby dużo jaśniejsze, gdyby nazwa brzmiała „TargetFrameworkList”.
howardlo
Przykład, na który wskazujesz, nie zawiera właściwości <TargetFrameworks>.
RenniePet
@RenniePet, dziękuję! Plik projektu zmienił się w czasie. Zmieniłem łącze na konkretne zatwierdzenie, w którym jest <TargetFrameworks>.
Nocny spacerowicz
12

Właściwie wybrałem bibliotekę klas (.NET Core).

To nie jest szablon projektu, którego potrzebujesz, jeśli Twoja biblioteka musi działać na wielu platformach docelowych. Dzięki temu szablonowi projektu Twoja biblioteka może być używana tylko w projekcie, który jest przeznaczony dla .NETCore. Podejście oparte na bibliotece PCL zostało wycofane, teraz musisz wybrać standard .NET.

Robisz to, uruchamiając projekt za pomocą szablonu projektu „Biblioteka klas (.NET Standard)”. Masz teraz możliwość wybrania wersji .NETStandard. Aktualna siatka kompatybilności jest tutaj .

Miejmy nadzieję, że będą aktualizować ten powiązany artykuł. To się zmienia, .NETStandard 2.0 został przybity, ale jeszcze nie jest dostępny. Planowany na drugi kwartał 2017 roku, prawdopodobnie do końca wiosny, obecnie pokazuje, że osiągnięto 97%. Słyszałem, jak projektanci mówili, że używanie 1.5 lub 1.6 nie jest zalecane, nie jest wystarczająco kompatybilne z 2.0

Hans Passant
źródło
Jak to działa, jeśli masz różne zależności dla różnych platform docelowych? Mam na myśli, project.jsonże możesz określić konkretne zależności dla docelowego frameworka.
Gigi
1
Jestem w 90% pewien, że musisz zapomnieć, że to kiedykolwiek istniało. To był zagmatwany bałagan, który był tylko środkiem zapobiegawczym w uruchomieniu .NETCore. Użyj siatki zgodności, z którą się połączyłem.
Hans Passant
Podejrzewałem to samo. Wielkie dzięki za wyjaśnienie!
Gigi
4
@HansPassant multi-target jest nadal najlepszą opcją, jeśli masz starszy kod, a jednocześnie programowanie od podstaw można wykonać w jednym z ostatnich pełnych wersji platformy lub dotnetcore.
Stefano Ricciardi,
1
Nawet greenfield nie jest jeszcze koniecznie przyjazny dla platformy .NET Core. Używam Azure Function Apps, ale ich wersja .NET Core obsługuje tylko kilka wyzwalaczy. (Potrzebuję wyzwalaczy Service Bus, więc utknąłem w .NET Framework, a bardzo duża biblioteka funkcji biznesowych może skończyć się jako multi-target). Platforma firmy Microsoft jest teraz poplątana. To nowoczesny odpowiednik DLL Hell.
McGuireV10
5

Zrobiłem przewodnik dla początkujących na temat struktury sieciowej i netcore z wieloma celami, który zaczyna się od prostej 1-liniowej poprawki, a następnie prowadzi Cię przez każdą z komplikacji.

Najprostszym podejściem jest ustawienie celu netcore lub netstandard działającego jako pierwszy. Następnie edytuj plik csproj i wykonaj te czynności dla innych celów.

  1. Dowiedz się o sekcjach warunkowych w pliku csproj, aby móc deklarować różne zależności dla każdego celu. Utwórz sekcje warunkowe dla każdego celu.
  2. Dodaj <Reference />sdla System. * Dlls dla dowolnych obiektów docelowych netframework, po prostu odczytując brak informacji o błędach kompilacji.
  3. Zajmij się zależnościami NuGet <PackageReference />sw przypadkach, gdy nie są one takie same dla każdego elementu docelowego. Najłatwiejszą sztuczką jest tymczasowe przywrócenie pojedynczego celu, tak aby GUI poprawnie obsługiwał odwołania do Nuget.
  4. Zajmij się kodem, który nie kompiluje się we wszystkich celach, ucząc się różnorodnych technik, obejść i oszczędzając czas.
  5. Wiedz, kiedy zmniejszyć straty, gdy koszt dodania większej liczby celów jest zbyt wysoki.
Chris F Carroll
źródło