Multi-targeting `net461` and `netcoreapp2.0` - c#

I have a command line .NET application that should compile against only ported .NET Core components when built as a .NET core app, but can additionally reference legacy .NET framework modules when built that way.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
<ProjectGuid>{48B4C337-CD13-4826-B6A1-DA97CE71511B}</ProjectGuid>
<RootNamespace>Microsoft.Pc</RootNamespace>
<AssemblyName>Pc</AssemblyName>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<OutputPath>$(PSdkFolder)\Binaries</OutputPath>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
<ProjectReference Include="..\CompilerCore\CompilerCore.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<ProjectReference Include="..\CompilerCore\CompilerCore.csproj" />
<ProjectReference Include="..\LegacyCompiler\LegacyCompiler.csproj" />
<ProjectReference Include="..\CompilerService\CompilerService.csproj" />
</ItemGroup>
</Project>
Despite this, I get these errors
"D:\Source\Repos\P\P.sln" (default target) (1) ->
"D:\Source\Repos\P\Src\Pc\CommandLine\CommandLine.csproj.metaproj" (default target) (20) ->
"D:\Source\Repos\P\Src\Pc\CommandLine\CommandLine.csproj" (default target) (21) ->
"D:\Source\Repos\P\Src\Pc\CommandLine\CommandLine.csproj" (Build target) (21:3) ->
(_GetProjectReferenceTargetFrameworkProperties target) ->
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\bin\Microsoft.Common.CurrentVersion.targets(1601,5): error : Project 'D:\Source\Repos\P\Src\Pc\LegacyCompiler\LegacyCompiler.csproj' targets 'net461'. It cannot be referenced by a project that targets '.NETCoreApp,Version=v2.0'. [D:\Source\Repos\P\Src\Pc\CommandLine\CommandLine.csproj]
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\bin\Microsoft.Common.CurrentVersion.targets(1601,5): error : Project 'D:\Source\Repos\P\Src\Pc\CompilerService\CompilerService.csproj' targets 'net461'. It cannot be referenced by a project that targets '.NETCoreApp,Version=v2.0'. [D:\Source\Repos\P\Src\Pc\CommandLine\CommandLine.csproj]
Why is it complaining that LegacyCompiler.csproj targets 'net461' while CommandLine targets '.NETCoreApp,Version=v2.0' when that dependency should be excluded in this case?
Update: these are the .csproj files for the other projects. As this project is open-source, I'm posting links to the files:
CompilerCore.csproj
CommandLine.csproj
LegacyCompiler.csproj

You should use .Net Standard class library with the version that is supported by both .Net Core and .Net 4.6.1 (as I may guess from your .csproj file).

Related

NuGet Msbuild Pack with non-SDK project

I have an "old style" .NET framework project which includes nuget references and other project references. Now I switched to the PackageReference format (removed the packages.config). I want to create a NuGet package for my project. So I added a reference to the "NuGet.Build.Tasks.Pack" package and used the MSBUILD pack target. In the first place it looked everything as expected, the resulting package contains all my references and the corresponding NuGet references. Now I have the problem, that I use also a project to project reference:
<ProjectReference Include="..\Wrapper\MyWrapper\MyWrapper.csproj">
<Project>{6b9a7dd0-b93f-3a5e-8fdf-99d0bf811652}</Project>
<Name>MyWrapper</Name>
</ProjectReference>
Based on the nuget documentation - for this reference:
Project to project references are considered by default as nuget
package references
But I want that this project reference is packaged into my package instead of a "nuget package reference". I found postings that using
PrivateAssets="all"
for the project reference could fix the problem, but adding this attribute to the project reference node does not change anything. Any help would be great!
I think you have missed something. It is not enough that you set PrivateAssets="all" for the ProjectReference. And actually, nuget will not view the referenced project as a nuget dependency and also nuget will not pack its assembly dll into the nupkg. You need other nodes.
Try these guidances:
Assume all your lib projects are target to net framework 4.7.2.
1) add the PrivateAssets="all" on the xxx.csproj file of the main project.
<ProjectReference Include="..\Wrapper\MyWrapper\MyWrapper.csproj">
<Project>{6b9a7dd0-b93f-3a5e-8fdf-99d0bf811652}</Project>
<Name>MyWrapper</Name>
<PrivateAssets>All</PrivateAssets>
</ProjectReference>
2) also add these node on the xxx.csproj file of the main project to pack the assembly dll into the nupkg:
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
</ItemGroup>
</Target>
3) then use this command to pack the project:
msbuild -t:rebuild,pack -p:PackageOutputPath=xxx\xxx -p:Authors="xxx" -p:Version="x.x.x"
Note: In my side, the main project called Mod and it references a project called Mod1. When I finish the pack process, you can see these in the nupkg.
It packs the refeneced dll as a lib rather than a nuget package.

Missing netstandard 2.0 assembly error on blank Xamarin.Forms project

I'm getting an error when creating a blank Xamarin.Forms project in VS where the netstandard assembly isn't being referenced, and cannot for the life of me figure out how to fix it.
The errors I'm getting in the app are:
CS0012 C# The type is defined in an assembly that is not referenced.
You must add a reference to assembly 'netstandard, Version=2.0.0.0,
Culture=neutral,PublicKeyToken=cc7b13ffcd2ddd51'
CS0006 C# Metadata file '..\bin\Debug\netstandard2.0\ref\App.dll'
could not be found)
I have tried, to no avail:
Updating the NETStandard.Library package to 2.0 or higher through NuGet (works for the App.iOS and App.Android projects but not the main App - it says "Blocked by project" for anything above v1.6.1)
Adding a reference to the requested netstandard version in the csproj
<Reference Include="netstandard"/> as suggested here: https://github.com/dotnet/standard/issues/542#issuecomment-344591026
Adding <Reference Include="System.IdentityModel"/> to the csproj as suggested here: https://github.com/dotnet/standard/issues/542#issuecomment-501309019
Changing <TargetFramework>netstandard2.0</TargetFramework> to
<TargetFrameworks>netstandard2.0</TargetFrameworks> as suggested here: https://github.com/dotnet/standard/issues/542#issuecomment-465375220
Deleting the bin, obj, .suo, and .vs folders and restarting VS, as well as
creating an entirely new Xamarin.Forms blank project
Updating Xamarin.Essentials and Xamarin.Forms in NuGet
Updating to VS Community 2019 16.5.0 Preview 2.0 (where I'd read this issue had been fixed; I've unfortunately misplaced the thread this was in)
This is my csproj (default with the exception of the AutoGenerateBindingRedirects, which was suggested in the comments):
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Forms" Version="4.3.0.908675" />
<PackageReference Include="Xamarin.Essentials" Version="1.3.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="ViewModels\" />
</ItemGroup>
</Project>
Anyone have any idea what to do about this? Most of the answers I've found either don't work or are several years old and are alleged to have been fixed with newer VS versions.
Here's a link to my detailed output logs:
VS Community 19 Preview: https://privatebin.net/?c7c4cd6123e0edd7#5qYTwaKSyGKBMgU3beshk3Xgx52nyCyAYcsq63uRfWWT
VS Community 19: https://privatebin.net/?2cf9d7ab07e8a4fb#5UkwWhxKFPDGnTtVdZuawkQLMRNe4qvJLeuaBrFrPoJf
Found the problem after two weeks of personal suffering and 2 hours spent with someone experienced with Xamarin! The issue was that I had two different versions of dotnet installed (one in the C: drive and one in my E: drive where I also keep program files). Entered dotnet --version in cmd only to see that the version it found was Ancient (1.x), and then dotnet --list-sdks to find the duplicate one. After deleting the old SDK in the E: drive and rechecking my dotnet version the error vanished!

Multi targetting net461 and netstandard - netstandard dependencies required even in net461 consumer

I'm working on a class library that I've multi-targetted to both net461 and netstandard2.0
One of the dependencies of this class library is Microsoft.ApplicationInsights
When it was targeting just net461, I could add a reference to Microsoft.ApplicationInsights (v2.4.0) via package manager console, or nuget ui, and it would add itself as a dependency.
Once I've multi-targetted the csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
</PropertyGroup>
</Project>
... if I try to add a reference, it asks me to accept license agreements for many, many dependencies.
Of course, I duly did so.
My issue comes when I package this class library as a nuget package.
Even if my consuming application targets net461, when I install this package, I am prompted to install all the netstandard dependencies - even if my consuming application doesn't target netstandard.
Is there a way to stop my net461 targetted package requiring all the dependencies for netstandard?
Have you tried using conditions in the project file to make some dependencies target framework specific? I've had similar sounding problems, though not with creating nuget packages, and this helped.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Reference Include="DependencyA" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="DependencyB" Version="1.0.0" />
</ItemGroup>
</Project>
Another idea would be to look at existing open source projects out there and see how they're solving it. Though finding one using Microsoft.ApplicationInsights may be trickier.

servicestack 4.5.14 & .Net Core 2.0

I was excited to read that the latest release version of ServiceStack (4.5.14) supports .Net Core 2.0 (see release notes). I tried adding ServiceStack 4.5.14 to my application targeting .Net Core 2.0.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.3" />
<PackageReference Include="ServiceStack" Version="4.5.14" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Local.Service\Local.Service.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.NETCore.App" Version="2.0.3" />
</ItemGroup>
</Project>
When I build the project, I get the following error:
Severity Code Description Project File Line Suppression State
Error CS1061 'IApplicationBuilder' does not contain a definition for 'UseServiceStack' and no extension method 'UseServiceStack' accepting a first argument of type 'IApplicationBuilder' could be found (are you missing a using directive or an assembly reference?) Startup ...\Startup\Startup.cs 24 Active
And warnings for each of the ServiceStack packages like this one:
Severity Code Description Project File Line Suppression State
Warning NU1701 Package 'ServiceStack 4.5.14' was restored using '.NETFramework,Version=v4.6.1' instead of the project target framework '.NETCoreApp,Version=v2.0'. This package may not be fully compatible with your project. Local.Service ...\Local.Service\Local.Service.csproj 1
The release notes to which I referred above reference example applications provided by the ServiceStack team. However, all of the examples that support .Net Core 2.0 are also using ServiceStack 5.0 and not 4.5.14.
I added ServiceStack 5.0 to my project, and it does work nicely with .Net Core 2.0. I would prefer to use a release version of the package, though, if possible.
Does v4.5.14 really support .Net Core 2.0? If so, how?
You need to use the ServiceStack.*.Core packages rather than the 4.5.14 packages.
If you read further through the release notes, you'll see that .NET Core packages still aren't merged into the regular release packages. That's planned for v5:
Merging the .NET Core packages into main packages where they’ll share
the same version and release cadence
The .NET Core 2.0 starter app is also deceptive as it references ServiceStack's MyGet feed and includes version 5 rather than 4.5.14.

How do you multi-target a .NET Core class library with csproj?

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

Categories

Resources