How do you multi-target a .NET Core class library with csproj?
Solution 1
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>
Solution 2
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
Solution 3
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
Solution 4
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:
- First, get a netcore or netstandard target working.
Then
-
Edit the .
csproj
project file and work through these steps for the other targets.- Change the
<TargetFramework>
tag to<TargetFrameworks>
and add your next target to the list, delimited by;
- Learn about conditional sections in your csproj file. Create one for each target. Use them to declare dependencies for each target.
- Add
<Reference />s
for System.* dlls for any netframework targets just by reading what the build error messages say is missing. - 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). - If you must: learn a creative variety of techniques, workarounds and timesavers to deal with code that doesn’t compile on all targets.
- Know when to cut your losses when the cost of adding more targets is too high.
- Change the
Related videos on Youtube
Comments
-
Gigi almost 3 years
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):-
user1703401 about 7 yearsThat is not what it is supposed to look like, you ought to get the .NETStandard 1.x choices listed in the dropdown. It is not very clear how this happened, be sure to pick the right project template to get started. Should be "Class Library (.NET Standard)". Looks like you picked the Console App template and then started changing properties, not the correct way. If you in fact used the Class Library template then the install did not go well.
-
Gigi about 7 yearsI actually selected Class Library (.NET Core).
-
user1703401 about 7 yearsRight, so that is the wrong one if you want to multi-target. You have to pick a .NETStandard to make the library usable on more than one platform.
-
Gigi about 7 yearsThat clears it. You can write up an answer from your comments if you like.
-
-
Gigi about 7 yearsHow does it work if you have different dependencies for different target frameworks? I mean in
project.json
you could specify specific dependencies for a target framework. -
user1703401 about 7 yearsI'm 90% sure that you need to forget that this ever existed. It was a confusing mess that was only a stop-gap measure to bootstrap .NETCore. Use the compatibility grid I linked to.
-
Gigi about 7 yearsI suspected as much. Thanks a lot for clarifying!
-
Gigi about 7 yearsDo you have to do this by editing the csproj manually, or can it be done via VS?
-
Aboo about 7 years@Gigi multi targeting needs to be done manually. Nuget packages can be done via VS2017 or manually.
-
Negorath about 7 yearsI was having the same issue, and everything worked fine after I added the s to <TargetFramework>. Really wish these things were better documented.
-
Asad Saeeduddin about 7 yearsI don't get it. You just removed the
TargetFramework
property, so why would you be able to use it in theItemGroup
condition attributes? When I try doing this in VS2017 I get squiggles everywhere$(TargetFramework)
is referenced. -
Aboo about 7 years@AsadSaeeduddin chances are that Resharper is showing you the squiggles and not Visual Studio. The $(TargetFramework) is just used to make an ItemGroup available to a particular framework/Moniker.
-
Asad Saeeduddin about 7 yearsJust tested it, you're right. Thanks! For posterity, I found a good explanation of all this stuff here: oren.codes/2017/01/04/…
-
jbtule about 7 years@Aboo I'd like to clarify that PCLs are not recommended going forward instead of .NETStandard, but multi-targeting a PCL in additional to .NETStandard is very necessary to move all of these nuget libraries over to .NETStandard. Nuget libraries depend on any number of hierarchy of nuget libraries, and while .NET Standrad works like a portable library, the API surface is much smaller and started out way smaller. The example project used Profile158 that allows .net4.5, same project could only move to .net standard 1.5 for it's api use, which only works on .net 4.6.2 or greater!
-
howardlo about 7 yearsThanks! It kills me. In Brian Kernigan's "The Elements of Programming Style" written four decades ago, he talks about the mistakes of using variables that differ by a single letter at the end. It would have been a lot clearer if the name was "TargetFrameworkList".
-
O. R. Mapper over 6 years"you can put the Nuget package references in a conditional ItemGroup (...) using VS Nuget Package Manager" - how? I don't see any option for that when I add a NuGet package reference.
-
Aboo over 6 years@O.R.Mapper if the NuGet package is constructed correctly this should happen automatically when you import. Meaning if NuGetPackageA supports multiple frameworks already, you don't need to put it in a conditioned item group. Now if you need to reference PackageA for .net framework and PackageB for .net core then that needs to be put in conditioned item group. There is no option in the interface as of today (Oct 2017).
-
InteXX over 6 years@HansPassant — "...you need to forget that this ever existed." Are you referring here to
project.json
? -
user1703401 over 6 yearsThat too. The recent updates for the full framework, Xamarin and UWP removed the last few obstacles to adopting .NETStandard 2.0. I am now 98.23% sure that multi-targeting is no longer useful. So forget this ever existed.
-
Stefano Ricciardi over 6 years@HansPassant multi-target is still the best option if you have legacy code and at the same time your greenfield development can be done in one of the recent full framework flavours or dotnetcore.
-
McGuireV10 over 6 yearsNot even greenfield is necessarily .NET Core friendly yet. I'm using Azure Function Apps but their .NET Core version only supports a couple of triggers. (I need Service Bus triggers, so I'm stuck with .NET Framework, and a very large business-functionality library may wind up as multi-target.) Microsoft's platform is a tangled mess right now. It's the modern equivalent of DLL Hell.
-
RenniePet about 6 yearsThe example you point at does not include a <TargetFrameworks> property.
-
Night walker about 6 years@RenniePet, thank you! The project file has changed in time. I changed the link to a concrete commit, where there is <TargetFrameworks>.
-
Lunyx almost 6 yearsThis should be much higher up. Everything else I had seen was talking about targeting multiple framework versions and having multiple versions of the library compiled for each framework.