如何使用 csproj 对.NETCore 类库进行多目标定位?

当.NET Core 仍然使用 project.json格式时,您可以构建一个类库 针对多个框架(例如 net451,netcoreapp1.0)。

既然正式的项目格式是使用 MSBuild 的 csproj,那么如何指定多个目标框架呢?我试图从 VS2017的项目设置中寻找这一点,但是我只能从。NET 核心框架(它甚至没有列出其他完整的。NET 框架版本,我已经安装了 ) :

enter image description here

57041 次浏览

You can manually edit .csproj file for this and set TargetFrameworks (not TargetFramework) property.

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

For example see EFCore.csproj: https://github.com/aspnet/EntityFrameworkCore/blob/951e4826a38ad5499b9b3ec6645e47c825fa842a/src/EFCore/EFCore.csproj

I actually selected Class Library (.NET Core).

That is not the project template you want if your library needs to work on multiple platform targets. With this project template, your library can only ever be used in a project that targets .NETCore. The PCL library approach was retired, you now have to pick a .NETStandard.

You do so by starting the project with the "Class Library (.NET Standard)" project template. You now have the option of picking the .NETStandard version. The current compatibility grid is here.

Hopefully they'll keep that linked article updated. This is in flux, .NETStandard 2.0 was nailed down but does not ship yet. Targeted for Q2 of 2017, end of spring probably, it currently shows as 97% done. I overheard the designers saying that using 1.5 or 1.6 is not recommended, not compatible enough with 2.0

You need to manually edit the project file and add s to the default TargetFramework and basically change it to TargetFrameworks. Then you mention the Moniker with a ; separator.

Also you can put the Nuget package references in a conditional ItemGroup manually or using VS Nuget Package Manager.

Here is what your .csproj should look like:

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

Another workaround I do these days because of missing documentation is that I create a project in VS2015 and form the project.json using the available documentation and intellisense, then open the solution in VS2017 and use the built-in upgrade. I will then look at the csproj file to figure out how to make that configuration happen.

Multi-targeting more esoteric targets without a Moniker:

Microsoft:

PCLs are not recommended+

Although PCLs are supported, package authors should support netstandard instead. The .NET Platform Standard is an evolution of PCLs and represents binary portability across platforms using a single moniker that isn't tied to a static like like portable-a+b+c monikers.

If you want to target a Portable Profile it doesn't have a predefined moniker so Portable Profiles also can't infer TargetFrameworkIdentifier, TargetFrameworkVersion, and TargetFrameworkProfile. Also a compiler constant isn't defined automatically. Finally you have to add all assembly references none are provided by default.

This Example below is taken from a project that used the dynamic keyword so it additionally needed the Microsoft.CSharp assembly, thus you can see how it's references for different targets.

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

I did a simple guide to multi-targeting net framework and netcore which starts with the minimum 15 second fix but then walks you through each of the complications.

The very simplest approach is:

  1. First, get a netcore or netstandard target working.

Then

  1. Edit the .csproj project file and work through these steps for the other targets.

    1. Change the <TargetFramework> tag to <TargetFrameworks> and add your next target to the list, delimited by ;
    2. Learn about conditional sections in your csproj file. Create one for each target. Use them to declare dependencies for each target.
    3. Add <Reference />s for System.* dlls for any netframework targets just by reading what the build error messages say is missing.
    4. Deal with NuGet <PackageReference />s dependencies in the cases where they are not the same for each target. (The easiest trick here is to temporarily revert to single targetting so that the GUI will just handle the Nuget references correctly for you).
    5. If you must: learn a creative variety of techniques, workarounds and timesavers to deal with code that doesn’t compile on all targets.
    6. Know when to cut your losses when the cost of adding more targets is too high.