Skip to content

Target netstandard2.1 for SignalR client #10540

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jun 13, 2019
Merged

Target netstandard2.1 for SignalR client #10540

merged 18 commits into from
Jun 13, 2019

Conversation

BrennanConroy
Copy link
Member

@BrennanConroy BrennanConroy commented May 25, 2019

Fixes #9525
Fixes #10607

@BrennanConroy BrennanConroy added the area-signalr Includes: SignalR clients and servers label May 25, 2019
@BrennanConroy BrennanConroy added this to the 3.0.0-preview6 milestone May 25, 2019
Copy link
Member Author

@BrennanConroy BrennanConroy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't change a couple libraries like Http.Connections.Common because the reason we cross-targeted that one was to not pull in the System.Text.Json package on netcoreapp3.0

#else
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
if (!isWindows)
try
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can do a compile time check here, for example, someone running on netcoreapp3.0 but targeting the netstandard2.0 library on windows 7 will have access to the managed websockets impl, but if we just hardcoded netstandard2.0 as not having websockets then a scenario that should work will not work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kinda sucks....

@@ -49,7 +49,7 @@ public partial class HubConnection
};

private static readonly MethodInfo _sendStreamItemsMethod = typeof(HubConnection).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Single(m => m.Name.Equals(nameof(SendStreamItems)));
#if NETCOREAPP3_0
#if NETSTANDARD2_1
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe all the #if NETSTANDARD2_1 checks should be #if !NETSTANDARD2_0, that way we might never have to update them in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doubt it matters. We can't be sure at this point when we'll drop .NET Standard 2.0 support, update 2.1 to something newer, add a new TFM, …

Copy link
Member

@JamesNK JamesNK May 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you can do is have ifdefs based on the available feature.

For example, this test could be #if HAVE_ASYNCENUMERABLE. Then in the csproj against NETSTANDARD2_1 you would define HAVE_ASYNCENUMERABLE.

Now when a new target is added in the future, e.g. netcoreapp50, you just add it to the csproj and set the required flags. No need to update individual source code files.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We know that all targets after netstandard2.0 will have these new APIs so I don't see why we'd add extra upkeep when a simple !NETSTANDARD2_0 check will do. And in the future if we remove netstandard2.0 compatibility then we just remove the #if entirely.

If for some crazy reason we do have future targets that remove features like IAsyncEnumerable then we can revisit and add feature flags.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future targets will never remove IAsyncEnumerable but future targets will add new features that you will likely want to use. With multiple targets things start getting complicated quickly...

A new feature in 3.0: #if !(NETSTANDARD2_0 || NETSTANDARD2_1)
A new feature in 4.0: #if !(NETSTANDARD2_0 || NETSTANDARD2_1 || NETSTANDARD3_0)
etc

Meanwhile using feature flags are a single test that and easy to read and all visible in the csproj to see what each target supports.

@@ -24,13 +24,26 @@ public static IAsyncEnumerable<T> MakeCancelableTypedAsyncEnumerable<T>(IAsyncEn
return new CancelableTypedAsyncEnumerable<T>(asyncEnumerable, cts);
}

#if NETCOREAPP3_0
public static async IAsyncEnumerable<object> MakeAsyncEnumerableFromChannel<T>(ChannelReader<T> channel, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
await foreach (var item in channel.ReadAllAsync(cancellationToken))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

channel.ReadAllAsync isn't available on netstandard2.1, is this something we should ask CoreFX to provide or...?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might get this for free with https://github.com/dotnet/corefx/issues/34963

@BrennanConroy BrennanConroy marked this pull request as ready for review May 27, 2019 19:18
<!-- Only update the default runtime version for preview builds. -->
<DefaultRuntimeFrameworkVersion Condition="'%(TargetFramework)' == '$(DefaultNetCoreTargetFramework)' and '$(IsServicingBuild)' != 'true'">$(MicrosoftNETCoreAppPackageVersion)</DefaultRuntimeFrameworkVersion>
<!-- Only update the targeting pack version for preview builds. -->
<TargetingPackVersion Condition="'%(TargetFramework)' == '$(DefaultNetCoreTargetFramework)' and '$(IsServicingBuild)' != 'true'">$(MicrosoftNETCoreAppPackageVersion)</TargetingPackVersion>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest moving the consistent Condition="'%(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'" to the <KnownFrameworkReference> element as is done below.

@@ -49,7 +49,7 @@ public partial class HubConnection
};

private static readonly MethodInfo _sendStreamItemsMethod = typeof(HubConnection).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Single(m => m.Name.Equals(nameof(SendStreamItems)));
#if NETCOREAPP3_0
#if NETSTANDARD2_1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doubt it matters. We can't be sure at this point when we'll drop .NET Standard 2.0 support, update 2.1 to something newer, add a new TFM, …

@BrennanConroy
Copy link
Member Author

We might be delaying this until preview7.

I'll probably spin up a PR with just the infrastructure changes.

@analogrelay
Copy link
Contributor

At this point unless we're super clear on what we're doing, deferring to preview 7 seems safest :).

@BrennanConroy
Copy link
Member Author

🆙 📅 This should be good now

public string TransportType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
}
namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to make these internal 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#10169 !

@BrennanConroy
Copy link
Member Author

Reviewers?

My main concern with this is that we only test the netcoreapp3.0 versions of the binaries.

Copy link
Contributor

@analogrelay analogrelay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only see one place where we are conditional on netcoreapp3.0 without also supporting netstandard2.1, what's the value of keeping that TFM in place? Is there actually different code we want to write?

@@ -24,13 +24,26 @@ public static IAsyncEnumerable<T> MakeCancelableTypedAsyncEnumerable<T>(IAsyncEn
return new CancelableTypedAsyncEnumerable<T>(asyncEnumerable, cts);
}

#if NETCOREAPP3_0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not possible in NETSTANDARD2_1?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

channel.ReadAllAsync isn't in netstandard2.1 currently. And they seem to not be interested in moving corefx to netstandard2.1 currently. https://github.com/dotnet/corefx/issues/34963

Copy link
Member

@JamesNK JamesNK Jun 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want an API added to netstandard2.1 you should request it on dotnet/standard, e.g. dotnet/standard#1144

(as far as I know. Worst case they can only say no 😋)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Channels isn't in netstandard at all, it's a library that targets netstandard2.0 (and doesn't cross-target 2.1).

My understanding of that issue though is that corefx isn't going to bulk-update to target 2.1. I think it doesn't preclude targeting 2.1 in a particular assembly.

If we really think netstandard2.1 is worth it here then I guess this is why we need to triple-target. It just seems counter-productive to keep doing this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what the world of developing libraries that run everywhere is like.

btw I still think you should use feature flags. If makes this stuff easier #10540 (comment)

#VoiceOfExperience

@BrennanConroy
Copy link
Member Author

what's the value of keeping that TFM in place?

@davidfowl is wanting to put the client in the shared framework.

@analogrelay
Copy link
Contributor

is wanting to put the client in the shared framework.

Does that require the netcoreapp3.0 TFM? I wouldn't think it should.

@davidfowl
Copy link
Member

Does that require the netcoreapp3.0 TFM? I wouldn't think it should.

I want no unnecessary deps when running on netcoreapp3.0 as well.

@analogrelay
Copy link
Contributor

I want no unnecessary deps when running on netcoreapp3.0 as well.

What unnecessary deps are there?

@davidfowl
Copy link
Member

What unnecessary deps are there?

  1. System.Threading.Channels is in the 3.0 shared framework.

I was also assuming we'd use the Microsoft.Bcl.AsyncInterfaces on ns2.0

@analogrelay
Copy link
Contributor

Right but why would embedding this package in the shared framework be a problem if all it's deps are in the shared fx? I don't understand how a separate build actually solves what seems to be a deployment problem. When this is embedded in the shared framework, it's dependencies will already be there so it should only add this single assembly. Even if we use the netstandard2.1 version that would be true.

The comment above about ChannelReader.ReadAllAsync not being present in netstandard2.1 may be the real reason we should triple-target but I don't see how your concerns warrant triple-targeting...

@@ -32,9 +32,11 @@ await foreach (var item in channel.ReadAllAsync(cancellationToken))
}
}
#else
// System.Threading.Channels.ReadAllAsync() is not available on netstandard2.0 and netstandard2.1
// But this is the exact same code that it uses
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was just going to ask for this after our convo 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BrennanConroy
Copy link
Member Author

@aspnet-hello
Copy link

This comment was made automatically. If there is a problem contact [email protected].

I've triaged the above build. I've created/commented on the following issue(s)
https://github.com/aspnet/AspNetCore-Internal/issues/2200

return true;
}
else
catch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know what get thrown in practice?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NotSupportedException

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured. This is gross, but catching only the NotSupportedException is probably even worse.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we cache this now?

@BrennanConroy
Copy link
Member Author

@aspnet/build Can someone merge please. Seems some kind of AzDo and GitHub integration is broken, everything is passing if you manually check.

@dougbu dougbu merged commit f989dac into master Jun 13, 2019
@ghost ghost deleted the brecon/21 branch June 13, 2019 05:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-signalr Includes: SignalR clients and servers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement IAsyncDisposable in Connection.Abstractions Update SignalR client to Target .Net Standard 2.1
8 participants