Docs: Adding a net461 target will also affect net472 and above

Created on 6 Nov 2018  Â·  9Comments  Â·  Source: dotnet/docs

CONSIDER adding a target for net461 when you're offering a netstandard2.0 target.

Using .NET Standard 2.0 from .NET Framework has some issues that were addressed in .NET Framework 4.7.2. You can improve the experience for developers that are still on .NET Framework 4.6.1 - 4.7.1

If net461 and netstandard2.0 targets are added all net framework consumers, including those which can also consume netstandard2.0 will consume the net461 target. This is not ideal, as the net461 target may have dependencies on netstandard1.x libraries or even restrictions on maximum versions of some dependencies.

Adding net461 can actually make the experience _worse_ for net472 and above consumers that would otherwise have been able to use the netstandard target. This disadvantage should be noted at the very least, but as time goes on, adding a net461 target becomes worse and worse advice, so maybe it shouldn't be a recommendation at all.

Ideally, the consumer would be able to pick from the compatible targets, but this isn't currently possible.


Document Details

⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Area - .NET Guide P2 Pri2 doc-enhancement dotneprod waiting-on-feedback

Most helpful comment

If a library is netstandard only, net472 will pick that. If a library has _any_ net framework targets, net472 will pick that instead.

The problem I had with an existing nuget package was that it had both netfx and netstandard targets, and the netfx target had a dependency on Microsoft.Extensions.Configuration < 2.0. I wanted to use the netstandard2.0 target, which had a dependency on >= 2.0 but I couldn't from a net472 project, because it only wants to use the full net framework target of the nuget package, even if that target is quite an old net framework e.g. a net472 project will pick a net451 target over netstandard2.0 in the same package.

I ended up having to get the package author to add a new target framework that met my needs, which could easily have been declined as a breaking change and more maintenance burden, but fortunately in this case they agreed to make the change, although it did add a significant delay before I could use it.

I realise that the ideal would have been that the existing netfx target wouldn't have had a package < 2.0 dependency, but it did because it existed before the 2.0 version of the Ms.Ex.Configuration package.

Just by adding a legacy net framework target, they also needed to add a newer target just so that newer consumers didn't have to pay the price. If they had left it on net standard only, it would have been better for a net472 consumer. Adding the legacy target may have made it better for users of older frameworks, but it comes at the cost of making it worse for users of newer frameworks, unless a newer target is also added.

On top of this, using net standard from < 472 was not a very good experience, with ~100 extra DLLs to ship, then there's the use or don't use System.Net.Http package, etc. We held off on using netstandard at all until 472 for those reasons, but now when packages have netfx and netstandard2.0 targets, we still have similar problems. A lot of these don't appear until you're 3 levels deep, with an application using a package which has its own dependencies, all on different target frameworks. Having packages target net standard promises to solve all that, but then having them also target net framework, and having that be used instead takes it away again.

If there is a way to get a net472 project with a SDK/PackageReference style csproj to pick the netstandard2.0 target of a nuget package that also has net4x targets, I couldn't find it.

It's kind of a package authoring problem, but because the consumer can't choose which compatible target to use, they can't fix it themselves.

All 9 comments

@JamesNK @terrajobst can you guys comment on this feedback?

We also have PR #9033 which is related to this that hasn't been merged yet.

@terrajobst is the person to talk to on this one

@ghelyar I'm not sure I follow.

If net461 and netstandard2.0 targets are added all net framework consumers, including those which can also consume netstandard2.0 will consume the net461 target. This is not ideal, as the net461 target may have dependencies on netstandard1.x libraries or even restrictions on maximum versions of some dependencies.

NuGet considers .NET Framework 4.6.1 compatible with .NET Standard 2.0, so if a package offers both, .NET Standard 1.x and 2.0, it will prefer the 2.0 asset. So providing a net461 specific binary should not downgrade .NET Standard 2.0 assets to 1.x.

Adding net461 can actually make the experience _worse_ for net472 and above consumers that would otherwise have been able to use the netstandard target. This disadvantage should be noted at the very least, but as time goes on, adding a net461 target becomes worse and worse advice, so maybe it shouldn't be a recommendation at all.

I'd generally expect the source code for .NET Standard 2.0 and .NET Framework 4.6.1 to be identical. You are correct in the observation that .NET Framework customers would never get the .NET Standard asset if the package provides any assets for .NET Framework though. So this would be more of a problem if a higher version of .NET Framework would support a higher version of .NET Standard 2 that 4.6.1, but that's not very likely as .NET Framework will probably never implement .NET Standard 2.1.

Ideally, the consumer would be able to pick from the compatible targets, but this isn't currently possible.

Could you elaborate what you mean by this? You mean the consumer should be able to select assets via metadata on the package reference?

If a library is netstandard only, net472 will pick that. If a library has _any_ net framework targets, net472 will pick that instead.

The problem I had with an existing nuget package was that it had both netfx and netstandard targets, and the netfx target had a dependency on Microsoft.Extensions.Configuration < 2.0. I wanted to use the netstandard2.0 target, which had a dependency on >= 2.0 but I couldn't from a net472 project, because it only wants to use the full net framework target of the nuget package, even if that target is quite an old net framework e.g. a net472 project will pick a net451 target over netstandard2.0 in the same package.

I ended up having to get the package author to add a new target framework that met my needs, which could easily have been declined as a breaking change and more maintenance burden, but fortunately in this case they agreed to make the change, although it did add a significant delay before I could use it.

I realise that the ideal would have been that the existing netfx target wouldn't have had a package < 2.0 dependency, but it did because it existed before the 2.0 version of the Ms.Ex.Configuration package.

Just by adding a legacy net framework target, they also needed to add a newer target just so that newer consumers didn't have to pay the price. If they had left it on net standard only, it would have been better for a net472 consumer. Adding the legacy target may have made it better for users of older frameworks, but it comes at the cost of making it worse for users of newer frameworks, unless a newer target is also added.

On top of this, using net standard from < 472 was not a very good experience, with ~100 extra DLLs to ship, then there's the use or don't use System.Net.Http package, etc. We held off on using netstandard at all until 472 for those reasons, but now when packages have netfx and netstandard2.0 targets, we still have similar problems. A lot of these don't appear until you're 3 levels deep, with an application using a package which has its own dependencies, all on different target frameworks. Having packages target net standard promises to solve all that, but then having them also target net framework, and having that be used instead takes it away again.

If there is a way to get a net472 project with a SDK/PackageReference style csproj to pick the netstandard2.0 target of a nuget package that also has net4x targets, I couldn't find it.

It's kind of a package authoring problem, but because the consumer can't choose which compatible target to use, they can't fix it themselves.

If a library is netstandard only, net472 will pick that. If a library has any net framework targets, net472 will pick that instead.

Could we please confirm if this is the case or not @terrajobst

Our libraries currently target net461 and netstandard2.0 - if the above is true we either need to drop net461 or add net472.

You can confirm with this tool https://nugettoolsdev.azurewebsites.net/5.3.0/get-nearest-framework?project=net472&package=net461%0D%0Anetstandard2.0

Or by just referencing the package from a net472 project, building it and looking at the output directory.

You could also add a package with obvious differences between net framework and net standard to see what you get, such as Microsoft.NET.Sdk.Functions.

For example, using the NuGet.Protocol package

var package = await Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json").GetResource<DependencyInfoResource>()
    .ResolvePackage(
        new PackageIdentity("Microsoft.NET.Sdk.Functions", new NuGetVersion("1.0.29")),
        NuGetFramework.Parse("net472"),
        new SourceCacheContext(),
        new NullLogger(),
        CancellationToken.None);
Console.WriteLine(package.Dependencies.First(d => d.Id == "Newtonsoft.Json").VersionRange.MinVersion);

prints 9.0.1, showing that for net472 it picked the net46 target over the netstandard2.0 target.

I believe you - my issue is that the whole netstandard and net framework situation seems to be a never ending source of problems.

As a library author I am trying to do the right thing (read: what I am told by Microsoft how to workaround all those issues). This is new to me - and it is frustrating that there is no official response to this issue + guidance.

My non-Microsoft-approved advice is to either target netstandard2.0;net461;net472 or only target netstandard2.0. The former is better if you want to support net 461-471 and also >= 472. The latter is fine if you don't really care about net461-471 consumers and just want a simpler package to maintain.

If you target netstandard2.0, net461 and net472 and use conditions in the csproj e.g. to change the dependencies of the package, include net472 with the netstandard2.0 conditions, as it should be compatible with them, at least for package references.

For example,

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net461;net472</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup Condition="$(TargetFramework) == 'netstandard2.0' or $(TargetFramework) == 'net472'">
    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.0.1" />
  </ItemGroup>

  <ItemGroup Condition="$(TargetFramework) == 'net461'">
    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="[1.0.0, 2.0)" />
  </ItemGroup>

</Project>

In this example, without the net472 target, users of net472 are restricted to Microsoft.Extensions.Configuration.Abstractions 1.X and so, for example, could not use it at all from an ASP.NET Core 2.X project targeting net472.

I raised this issue with the documentation because the existing recommendation considers the experience for users of 461-471 but does not consider the impact this has on 472 and above, and there is a misconception that net472 will pick netstandard2.0 over net461, so package authors tend not to realise that when they follow the recommendation and add a target for net461 they are affecting all versions of .net framework >= 4.6.1, even if they have a netstandard2.0 target.

Tomorrow this issue is one year old - I guess "non-Microsoft" approved is all we get ;)

Thanks for your work!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ite-klass picture ite-klass  Â·  3Comments

gmatv picture gmatv  Â·  3Comments

svick picture svick  Â·  3Comments

Eilon picture Eilon  Â·  3Comments

garfbradaz picture garfbradaz  Â·  3Comments