Bagaimana Anda menargetkan multi-pustaka kelas .NET Core dengan csproj?

96

Ketika .NET Core masih menggunakan project.jsonformat tersebut, Anda bisa membangun perpustakaan kelas yang menargetkan beberapa kerangka kerja (misalnya net451, netcoreapp1.0).

Sekarang format proyek resmi csprojmenggunakan MSBuild, bagaimana Anda menentukan beberapa kerangka kerja untuk ditargetkan? Saya mencoba untuk mencari ini dari pengaturan proyek di VS2017, tapi saya bisa hanya menargetkan kerangka tunggal dari kerangka NET Inti (bahkan tidak daftar lengkap versi .NET Framework lain yang saya lakukan telah menginstal) :

masukkan deskripsi gambar di sini

Gigi
sumber
Seharusnya tidak seperti itu, Anda harus mendapatkan pilihan .NETStandard 1.x yang terdaftar di dropdown. Tidak begitu jelas bagaimana ini terjadi, pastikan untuk memilih template proyek yang tepat untuk memulai. Harus "Perpustakaan Kelas (.NET Standard)". Sepertinya Anda memilih template Aplikasi Konsol lalu mulai mengubah properti, bukan dengan cara yang benar. Jika Anda sebenarnya menggunakan template Perpustakaan Kelas maka penginstalan tidak berjalan dengan baik.
Hans Passant
Saya sebenarnya memilih Class Library (.NET Core).
Gigi
2
Benar, jadi itu salah jika Anda ingin multi-target. Anda harus memilih .NETStandard agar perpustakaan dapat digunakan di lebih dari satu platform.
Hans Passant
Itu menyelesaikannya. Anda dapat menulis jawaban dari komentar Anda jika Anda suka.
Gigi

Jawaban:

122

Anda perlu secara manual mengedit file proyek dan add s ke default TargetFramework dan pada dasarnya mengubahnya ke TargetFrameworks . Kemudian Anda menyebutkan Moniker dengan ; pemisah.

Anda juga dapat meletakkan referensi paket Nuget dalam ItemGroup bersyarat secara manual atau menggunakan VS Nuget Package Manager.

Berikut tampilan .csproj Anda:

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

Solusi lain yang saya lakukan hari ini karena dokumentasi yang hilang adalah saya membuat proyek di VS2015 dan membentuk project.json menggunakan dokumentasi dan intellisense yang tersedia, kemudian membuka solusi di VS2017 dan menggunakan pemutakhiran bawaan. Saya kemudian akan melihat file csproj untuk mencari tahu bagaimana membuat konfigurasi itu terjadi.

Multi-penargetan target yang lebih esoterik tanpa Moniker :

Microsoft:

PCL tidak disarankan +

Meskipun PCL didukung, pembuat paket harus mendukung standar bersih sebagai gantinya. .NET Platform Standard adalah evolusi dari PCL dan mewakili portabilitas biner di seluruh platform menggunakan moniker tunggal yang tidak terikat dengan statis seperti moniker portabel-a + b + c.

Jika Anda ingin menargetkan Profil Portabel itu tidak memiliki ditentukan sebelumnya moniker sehingga Profiles portabel juga tidak bisa mengambil kesimpulan TargetFrameworkIdentifier, TargetFrameworkVersiondan TargetFrameworkProfile. Juga konstanta kompilator tidak didefinisikan secara otomatis. Akhirnya Anda harus menambahkan semua referensi assembly tidak ada yang disediakan secara default.

Contoh di bawah ini diambil dari proyek yang menggunakan dynamickata kunci sehingga diperlukan tambahan Microsoft.CSharpassembly, sehingga Anda dapat melihat bagaimana referensi untuk target yang berbeda.

<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
sumber
1
Apakah Anda harus melakukan ini dengan mengedit csproj secara manual, atau dapatkah itu dilakukan melalui VS?
Gigi
1
@Gigi multi penargetan perlu dilakukan secara manual. Pengemasan nuget dapat dilakukan melalui VS2017 atau secara manual.
Aboo
2
Saya mengalami masalah yang sama, dan semuanya berfungsi dengan baik setelah saya menambahkan s ke <TargetFramework>. Benar-benar berharap hal-hal ini didokumentasikan dengan lebih baik.
Negorath
2
@AsadSaeeduddin kemungkinan besar Resharper menunjukkan coretan dan bukan Visual Studio. $ (TargetFramework) hanya digunakan untuk membuat ItemGroup tersedia untuk kerangka / Moniker tertentu.
Aboo
2
@ORMapper jika paket NuGet dibuat dengan benar, ini akan terjadi secara otomatis saat Anda mengimpor. Artinya jika NuGetPackageA sudah mendukung banyak kerangka kerja, Anda tidak perlu memasukkannya ke dalam grup item yang dikondisikan. Sekarang jika Anda perlu mereferensikan PackageA untuk .net framework dan PackageB untuk .net core maka itu perlu dimasukkan ke dalam grup item yang dikondisikan. Tidak ada opsi di antarmuka mulai hari ini (Okt 2017).
Aboo
24

Anda dapat secara manual mengedit .csprojfile untuk ini dan mengatur properti TargetFrameworks(bukan TargetFramework).

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

Misalnya lihat EFCore.csproj: https://github.com/aspnet/EntityFrameworkCore/blob/951e4826a38ad5499b9b3ec6645e47c825fa842a/src/EFCore/EFCore.csproj

Pejalan malam
sumber
9
Terima kasih! Ini membunuhku. Dalam "The Elements of Programming Style" Brian Kernigan yang ditulis empat dekade lalu, dia berbicara tentang kesalahan penggunaan variabel yang berbeda dengan satu huruf di bagian akhir. Akan jauh lebih jelas jika namanya adalah "TargetFrameworkList".
howardlo
Contoh yang Anda tunjuk tidak menyertakan properti <TargetFrameworks>.
RenniePet
@RenniePet, terima kasih! File proyek telah berubah seiring waktu. Saya mengubah tautan menjadi komit konkret, di mana ada <TargetFrameworks>.
Pejalan kaki malam pada
12

Saya sebenarnya memilih Class Library (.NET Core).

Itu bukan template proyek yang Anda inginkan jika perpustakaan Anda perlu bekerja pada beberapa target platform. Dengan template proyek ini, pustaka Anda hanya dapat digunakan dalam proyek yang menargetkan .NETCore. Pendekatan perpustakaan PCL dihentikan, Anda sekarang harus memilih .NETStandard.

Anda melakukannya dengan memulai proyek dengan template proyek "Perpustakaan Kelas (.NET Standard)". Anda sekarang memiliki opsi untuk memilih versi .NETStandard. Kisi kompatibilitas saat ini ada di sini .

Mudah-mudahan mereka akan terus memperbarui artikel tertaut itu. Ini berubah, .NETStandard 2.0 telah dipaku tetapi belum dikirimkan. Ditargetkan untuk Q2 tahun 2017, mungkin akhir musim semi, saat ini menunjukkan 97% selesai. Saya tidak sengaja mendengar desainer mengatakan bahwa menggunakan 1.5 atau 1.6 tidak disarankan, tidak cukup kompatibel dengan 2.0

Hans Passant
sumber
Bagaimana cara kerjanya jika Anda memiliki dependensi yang berbeda untuk kerangka kerja target yang berbeda? Maksud saya, project.jsonAnda dapat menentukan dependensi khusus untuk kerangka target.
Gigi
1
Saya 90% yakin Anda harus melupakan bahwa hal ini pernah ada. Itu adalah kekacauan yang membingungkan yang hanya merupakan ukuran stop-gap untuk bootstrap .NETCore. Gunakan kisi kompatibilitas yang saya tautkan.
Hans Passant
Aku juga curiga. Terima kasih banyak telah mengklarifikasi!
Gigi
4
@HansPassant multi-target masih merupakan pilihan terbaik jika Anda memiliki kode lama dan pada saat yang sama pengembangan greenfield Anda dapat dilakukan di salah satu dari kerangka lengkap terbaru atau dotnetcore.
Stefano Ricciardi
1
Bahkan greenfield belum tentu ramah .NET Core. Saya menggunakan Aplikasi Fungsi Azure tetapi versi .NET Core mereka hanya mendukung beberapa pemicu. (Saya memerlukan pemicu Bus Layanan, jadi saya terjebak dengan .NET Framework, dan pustaka fungsionalitas bisnis yang sangat besar mungkin berakhir sebagai multi-target.) Platform Microsoft sedang berantakan saat ini. Ini setara dengan DLL Hell modern.
McGuireV10
5

Saya melakukan panduan pemula untuk kerangka kerja multi-penargetan dan netcore yang dimulai dengan perbaikan 1 baris sederhana dan kemudian memandu Anda melalui setiap komplikasi.

Pendekatan paling sederhana adalah mendapatkan target netcore atau netstandard pertama kali. Kemudian edit file csproj dan lakukan langkah-langkah berikut untuk target lainnya.

  1. Pelajari tentang bagian bersyarat di file csproj Anda, sehingga Anda bisa mendeklarasikan dependensi yang berbeda untuk setiap target. Buat bagian bersyarat untuk setiap target.
  2. Tambahkan <Reference />suntuk System. * Dll untuk target netframework hanya dengan membaca pesan kesalahan build yang hilang.
  3. Tangani dependensi NuGet jika dependensi <PackageReference />stidak sama untuk setiap target. Trik termudah adalah untuk sementara kembali ke penargetan tunggal sehingga GUI akan menangani referensi Nuget dengan benar untuk Anda.
  4. Tangani kode yang tidak terkompilasi pada semua target, dengan mempelajari berbagai teknik kreatif, solusi, dan penghemat waktu.
  5. Ketahui kapan harus memotong kerugian Anda ketika biaya penambahan lebih banyak target terlalu tinggi.
Chris F Carroll
sumber