rabbitmq-dotnet-client icon indicating copy to clipboard operation
rabbitmq-dotnet-client copied to clipboard

Re-evaluate supported .NET products / C# language version

Open lukebakken opened this issue 2 years ago • 18 comments

Version 7 of this client supports netstandard2.0, which prevents the use of features like MemoryPool.

Before shipping version 8, supported frameworks and C# language version should be re-evaluated.

Discussion: https://github.com/rabbitmq/rabbitmq-dotnet-client/pull/1347#discussion_r1398208131

lukebakken avatar Nov 19 '23 13:11 lukebakken

,NET Framework will be supported for the foreseeable future and it's only .NET Standard 2.0 compliant., So, .NET Standard 2.0 should be supported.

Any types introduced because of .NET Standard 2.0 should provide implicit conversions and overloads for .NET (6+) types.

paulomorgado avatar Nov 19 '23 16:11 paulomorgado

On the other hand, I think there's an increasingly good case for dropping .NET Framework support, especially if you can keep an older version "supported" for perhaps an extended amount of time to offer a version that keeps working on .NET Framework.

As more and more .NET releases come out, the gap between them and and .NET Framework keeps growing, making it impossible (or incredibly painful) to take advantage of the new features being offered.

Locking to .NET Standard 2.0 (2.1 is a completely dead end, don't even look at it) is an anchor holding back actual innovation.

bording avatar Nov 19 '23 17:11 bording

.NET Standard 2.1 was a band-aid in the evolution process. It was never supposed to exist, but some runtimes were late to move to .NET 5.0.

You have to consider the cost of keeping older versions supported against having conditional code for newer versions. even if with limited features or capabilities.

,NET Framework (.NET Standard 2.0) will be supported, at least, while Windows Server 2022 is supported, which is Oct. 31 2031.

paulomorgado avatar Nov 19 '23 21:11 paulomorgado

Problem is that ms slowly stopping support .NET Standard 2.0 properly in modern nuget packages and slowly go away from it

For example: one of the dependency for RabbitMQ.Client image

long story short: Support net framework via .NET standard 2.0 is not a good idea in long perspective, better to directly support as we have it in version 6

I would recommend support directly: .NET 6+ (minimal supported version of .NET by MS) or newer .NET Framework 4.6.2 (minimal supported .NET Framework by MS) or newer and remove .NET Standard 2.0 support

<TargetFrameworks>net6.0;net462</TargetFrameworks>

Romfos avatar Jan 06 '24 13:01 Romfos

I'd say remove .NET Framework support completely for v8.0 onwards, and just target whatever is the then currently supported LTS version, as long as 7.x (ehich would be the last .NET Framework supported release) continues to get bugfixes backported as appropriate.

stebet avatar Jan 06 '24 13:01 stebet

@Romfos,

Problem is that ms slowly stopping support .NET Standard 2.0 properly in modern nuget packages and slowly go away from it

What evidence do you have of that?

All supported versions of .NET Framework are fully .NET Standard 2.0 compliant. That should be the target if it meets all needs. Otherwise, it should be the lowest .NET Framework version.

.NET Framework will be around for a long time. .NET Framework 3.5 SP1 is supported until 2029. Imagine for how long .NET Framework 4.8 will be supported.

paulomorgado avatar Jan 06 '24 19:01 paulomorgado

@paulomorgado

you can check my screenshot above about warning for support and testing

some additional articles:

  • https://andrewlock.net/stop-lying-about-netstandard-2-support/
  • https://github.com/open-telemetry/opentelemetry-dotnet/issues/3448

Romfos avatar Jan 07 '24 19:01 Romfos

@Romfos, that screenshot is referring to .NET Core 2.0, not .NET Standard 2.0.

paulomorgado avatar Jan 07 '24 19:01 paulomorgado

@paulomorgado this screenshot related to .net standard support for some runtimes like .net 5, .net core 3.1, .net fx .4.6.1

Romfos avatar Jan 07 '24 19:01 Romfos

@Romfos,

NetFx461 was declared mostly, but not fully implementing .NET Standard 2.0. But 4.6.2 and forward are. And those are the ones supported.

.NET Core 3.1 and .NET 5.0 are no longer supported. But they do fully implement .NET Standard 2.0.

I can't see any of your claims in there. What am I missing here?

paulomorgado avatar Jan 07 '24 20:01 paulomorgado

@paulomorgado

that is the one of the problem. you can check links from my post above

note: no difference for .net standard 2.0 support for net fx 4.6.1 and 4.6.2 (probably you mixed up it with 4.7.2)

Romfos avatar Jan 08 '24 21:01 Romfos

@Romfos - please provide a CONCRETE example of what you're stating, using the latest version 7 alpha of this library. In other words, I should be able to clone a repository, run dotnet build / dotnet run, and see an issue with using netstandard2.0 as a target framework for this library. A build warning does not suffice - it has to be an issue while actually using this library.

The first article to which you link here talks about issues with netstandard2.0 and .NET Core 2 / 3. Both of those are out of support and do not apply to the discussion here. Team RabbitMQ does not have the resources to support any out-of-support frameworks/runtimes.

The second article to which you link, does bring up some interesting points.

lukebakken avatar Jan 08 '24 21:01 lukebakken

@lukebakken

  1. I don't have CONCRETE example for version 7 that you can build + run
  2. Support old runtimes - this is not what i propose
  3. Yes, NET Framework 4.6.1 support .NET Standard - i do not try to opposite to it. this is out of scope
  4. I do not propose to drop .NET Framework

let me try to explain

State of .NET Standard 2.0 in 2023/2024:

.NET implementation Version support Status Comment
.NET and .NET Core 2.0, 2.1, 2.2, 3.0, 3.1, 5.0, 6.0, 7.0, 8.0 Supported Support .NET 6+ by ms
.NET Framework 4.6.1, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8, 4.8.1 Supported Support .NET Framework 4.6.2+ by ms
Mono 5.4, 6.4 Only security hotfixes
Xamarin.iOS 10.14, 12.16 Deprecated in May 2024 .NET MAUI is a replacement for .NET 6+
Xamarin.Mac 3.8, 5.16 Deprecated in May 2024 .NET MAUI is a replacement for .NET 6+
Xamarin.Android 8.0, 10.0 Deprecated in May 2024 .NET MAUI is a replacement for .NET 6+
Universal Windows Platform 10.0.16299 Only security hotfixes Windows App SDK is a replacement for .NET 6+
Unity 2018.1 Supported .NET 6 migration planned for next release
Windows Phone n\a Deprecated
Windows Phone Silverlight n\a Deprecated

As you can see most of .NET Standard runtimes (except of NET FX + .NET 6+) are no longer actively developed, depreciated or will be depreciated soon

How Microsoft see future of .NET Standard: https://devblogs.microsoft.com/dotnet/the-future-of-net-standard/

Packages that Microsoft are planning to stop supporting for .NET Standard:

  • ML.NET (https://github.com/dotnet/machinelearning/pull/6749)
  • Npgsql (https://github.com/npgsql/npgsql/issues/5296)
  • Orleans (https://github.com/dotnet/orleans/pull/6984)
  • even WCF Client (https://github.com/dotnet/docs/issues/33515) ... slowly, but they move away from it. I think it will be faster after Xamarin deprecation in May 2024.

Issues with .NET Standard in 2023/2024:

.NET Standard is now meaningless (https://andrewlock.net/stop-lying-about-netstandard-2-support/)

Original idea of .NET Standard: if runtime support .NET Standard of specific version and library support it then you can run this library on this runtime

How it works in 2023:

  • Some nuget packages could block some runtimes (mostly older, but it depends on package owner) via build error\warnings for specific targets.
  • Some nuget packages declare NS support, but throw PlatformNotSupportedException for net standard version. At the same time they have correct implementation for specific runtimes. (https://github.com/open-telemetry/opentelemetry-dotnet/issues/3448#issuecomment-1185443377)
  • Some nuget packages declare NS support, but tests only for specific runtimes, NS version is without testing as provided as is (for example if package have mutitargeting)
  • If NS package is transitive dependency it could drop some runtime during update and it is out of your control.

Benefits from multitargeting (.NET FX, .NET 6) instead of .NET Standard in 2023:

  • You can explicitly declare supported platforms and minimal versions of it
  • All platforms that supported - they are tested
  • No problem (or less) with transitive dependencies

My private opinion:

  • Make sense to make .NET 6+ primary target platform
  • .NET Framework is still important at least for the next several years and maybe make sense to support it (at least provide hotfixes for 7.0 branch as minimum, or "full power" support for new features as maximum)
  • All other runtimes - who care???
  • .NET Standard is dead from "conceptional point of view", because most of runtimes that is was supported originally are depreciated (or soon depreciated) and replaced by set of technologies based on .NET 6+
  • Better to support .NET Framework directly, not via .NET Standard. (You can specify minimal version explicitly + less potential less problems with dependencies)

Romfos avatar Jan 09 '24 09:01 Romfos

@stephentoub could you please help share your opinion about this, appreciate it

WeihanLi avatar Jan 09 '24 09:01 WeihanLi

@Romfos, you really should research a bit more. .NET Standard is not a runtime.

paulomorgado avatar Jan 09 '24 12:01 paulomorgado

@viktorhofer

stephentoub avatar Jan 09 '24 13:01 stephentoub

Hello all. I'm from the .NET team and should be the right person to comment on the above.

I agree with most of what @Romfos mentioned here. Similarly to PCLs (portable class libraries), .NET Standard is an API contract that is executed on specific runtimes. The runtimes that are currently in-support are mentioned above. As you can see, the in-support runtimes that we as the .NET team focus on are .NET >= 6 and .NET Framework >= 4.6.2.

Here's an overview of types of packages that the .NET team publishes:

Package type Description
Broad reach ecosystem package Libraries like System.Text.Json are consumable on net6.0, netstandard2.0 and net462. S.T.J. still supports .NET Standard as it's heavily used throughout the ecosystem.
.NET only package Libraries decided to drop support for runtimes other than .NET, i.e. Microsoft.EntityFrameworkCore/5.0.17 supported all netstandard2.1 compatible runtimes, but started only supporting .NET 6 and newer with its 6.0.0 release.
ASP.NET app model package Libraries like Microsoft.AspNetCore.WebUtilities which only support the current release (TFM = net8.0).
MSBuild task package MSBuild task packages which contribute to the build and usually target both .NET and .NET Framework in order to be invokable on the different flavours of msbuild (.NET SDK vs part of .NET Framework based tooling).

While some of our components actively move away from .NET Standard, others that we call "broad-reach ecosystem packages" keep their .NET Standard support to avoid disrupting the .NET ecosystem.

While we in the past recommended to leverage .NET Standard for a shared codebase, we nowadays recommend to multi-target for .NET Framework and .NET in order to be able to target the latest API surface area on .NET while still supporting .NET Framework.

This effectively means that you want to at least target the minimum in support runtimes: net6.0 and net462. Libraries might also want to target the very latest .NET for max perf and/or AOT compatibility. If you still want to support .NET Standard class libraries, add netstandard2.0 to that list of TFMs. Don't target out-of-support runtimes, i.e. for security reasons.

ViktorHofer avatar Jan 09 '24 14:01 ViktorHofer

While we in the past recommended to leverage .NET Standard for a shared codebase, we nowadays recommend to multi-target for .NET Framework and .NET in order to be able to target the latest API surface area on .NET while still supporting .NET Framework by.

Mainly because .NET Standard 2.0 is frozen and won't have anything more added to it and all new APIs and innovation are in .NET.

Starting with .NET 6.0, the runtime is also the standard.

paulomorgado avatar Jan 09 '24 15:01 paulomorgado

Not commenting on the rest of the discussion about which TFMs we should support here, but the specific impetus of this was that MemoryPool<T> is not supported in netstandard2.0, but it looks like it is, acc. to the documentation; you just need to add the System.Memory package.

For example, with this .csproj:

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

  <PropertyGroup>
    <TargetFrameworks>net8.0;net6.0;netstandard2.0</TargetFrameworks>
    <LangVersion>12</LangVersion>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>

  <ItemGroup Condition="$(TargetFramework) == 'netstandard2.0'">
    <PackageReference Include="System.Memory" Version="4.6.0" />
  </ItemGroup>

</Project>

this compiles:

using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;

namespace MemoryPoolLib;

public class MyMem : MemoryPool<byte>
{
    internal readonly byte[] _buffer = new byte[4096];
    private readonly List<Block> _blocksAvailable = [];
    private const int DefaultSize = 8;
    public override int MaxBufferSize => 1024;

    public override IMemoryOwner<byte> Rent(int minBufferSize = -1)
    {
        int size = minBufferSize > 0 ? minBufferSize : DefaultSize;
        var block = _blocksAvailable.FirstOrDefault(b => b.Size >= size);
        Block newBlock = new(block.Start, size);
        _blocksAvailable.Remove(block);
        if (block.Size > size)
        {
          Block leftoverBlock = new(block.Start + size, block.Size - newBlock.Size);
          _blocksAvailable.Add(leftoverBlock);
        }
        return new RentedBlock(this, newBlock);
    }

    protected override void Dispose(bool disposing) { }

    internal void Return(Block block)
    {
        // TODO: merge with other contiguous blocks
        _blocksAvailable.Add(block);

    }

}

public record struct Block(int Start, int Size) { }
public class RentedBlock  : IMemoryOwner<byte>
{
    private readonly MyMem _pool;
    private readonly Block _block;
    private readonly Memory<byte> _slice;
    internal RentedBlock(MyMem pool, Block block)
    {
      _pool = pool;
      _block = block;
      _slice = ((Memory<byte>)_pool._buffer).Slice(_block.Start, _block.Size);
    }

    public Memory<byte> Memory => _slice;

    public void Dispose()
    {
        _pool.Return(_block);
    }
}

I was able to compile this with the .NET 8 SDK, and then reference the NuGet package with Mono's msbuild on a .NET 4.8 project, and it worked just fine. Also, the .NET 8 project that consumes this NuGet package compiles just fine and doesn't add the extra System.Buffers package.

iinuwa avatar Nov 01 '24 18:11 iinuwa

@iinuwa thank you for taking the time to dig in.

michaelklishin avatar Nov 01 '24 18:11 michaelklishin