When .NET Core still used the project.json
format, you could build a class library targeting multiple frameworks (e.g. net451, netcoreapp1.0).
Now that the official project format is csproj
using MSBuild, how do you specify multiple frameworks to target? I am trying to look for this from the project settings in VS2017, but I am able to only target a single framework from the .NET Core frameworks (it doesn't even list the other full .NET Framework versions which I do have installed):
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>