Skip to content

System.Interactive.Async: consume Microsoft.Bcl.AsyncInterfaces for all suitable TFMs #950

@mgravell

Description

@mgravell

Currently the build targets defines this via:

 Condition="'$(TargetFramework)' == 'netcoreapp3.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'netstandard2.0'"

However, the Microsoft.Bcl.AsyncInterfaces package provides these for other TFMs (preview, waiting on .NET Core 3). If the APIs aren't unified, it is going to cause confusion for consumers, especially if they take transitive dependencies via any path that gives them both Microsoft.Bcl.AsyncInterfaces and System.Interactive.Async on any of the impacted TFMs.

Ultimately, having two sources of truth for these APIs is just a disaster for consumers. The extern alias hack exists, but is a pain to explain to users - who expect install-package to just work.

I would argue that Microsoft.Bcl.AsyncInterfaces is a more "official" source of truth than System.Interactive.Async (despite System.* vs Microsoft.*), and thus System.Interactive.Async must yield.

To repeat my analysis from here, the most likely outcome is:

  • .NET Standard 2.1: type-forward to System.Runtime
  • .NET Core 3.0 - type-forward to System.Runtime
  • .NET Standard 2.0: type-forward to Microsoft.Bcl.AsyncInterfaces
  • (new TFM) .NET Framework 4.6.1: type-forward to Microsoft.Bcl.AsyncInterfaces
  • .NET Framework 4.6: define locally (but: correctly)
  • .NET Framework 4.5: define locally (but: correctly)
  • .NET Standard 1.3: define locally (but: correctly)
  • .NET Standard 1.0: define locally (but: correctly)

(with a slightly simpler setup possible if we can collectively convince Microsoft to not omit any TFMs that System.Interactive.Async targets)

There is a knowledge gap (for me) here about what happens if MonoTouch1.0, for example, targets both (directly or indirectly); there is an explicit TFM for this in Microsoft.Bcl.AsyncInterfaces, but if "reactive" ends up providing the .NET Standard 1.0 or .NET Standard 1.3 build: the problem is back. If that is the case, I would say that it provides grounds for either:

  1. we lean on Microsoft.Bcl.AsyncInterfaces to provide net46, net45, netstandard1.0 and netstandard1.3 TFMs, and remove the interfaces completely from System.Interactive.Async
  2. "reactive" should provide explicit TFMs for everything that Microsoft.Bcl.AsyncInterfaces provides TFMs for (type-forwarding to Microsoft.Bcl.AsyncInterfaces), to ensure no danger zones

My preferred option is "1", and IMO the risk of customer/consumer impact by continuing to have multiple definitions should be a fair battering ram for adding them. That would allow the cleaner:

  • .NET Standard 2.1: type-forward to System.Runtime
  • .NET Core 3.0 - type-forward to System.Runtime
  • .NET Standard 2.0: type-forward to Microsoft.Bcl.AsyncInterfaces
  • .NET Framework 4.6: type-forward to Microsoft.Bcl.AsyncInterfaces
  • .NET Framework 4.5: type-forward to Microsoft.Bcl.AsyncInterfaces
  • .NET Standard 1.3: type-forward to Microsoft.Bcl.AsyncInterfaces
  • .NET Standard 1.0: type-forward to Microsoft.Bcl.AsyncInterfaces

which doesn't have any danger zones for transitives, since all "reactive" TFMs are provided for by the "official" sources.

Happy to try and help with a PR on the local changes if you think this unification is the way to go.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions