Closed
Description
Background and Motivation
When approving API for #5280 we considered replacing Single
with new Caller
and new Client
members but we were worried it would be source breaking because:
- The suggestion of using
new
Caller
andClient
methodsISingleClientProxy
would be source breaking with existingIHubClients
andIHubCallerClients
implementations, decorators etc... if someone replace theIHubContext<>
in DI, so we'll stick withSingle
However further investigation shows that we can add new
Caller
and Client
members without breaking existing IHubClients
and IHubCallerClients
implementations unless they're being used for client results which never worked before .NET 7 making the change both non-binary and non-source breaking[1].
Proposed API
namespace Microsoft.AspNetCore.SignalR;
public interface IHubClients<T>
{
- T Single(string connectionId) => throw new NotImplementedException();
}
public interface IHubClients : IHubClients<IClientProxy>
{
- new ISingleClientProxy Single(string connectionId) => throw new NotImplementedException();
+ new ISingleClientProxy Client(string connectionId) => new NonInvokingSingleClientProxy(((IHubClients<IClientProxy>)this).//...
}
public interface IHubCallerClients : IHubCallerClients<IClientProxy>
{
- new ISingleClientProxy Single(string connectionId) => throw new NotImplementedException();
+ new ISingleClientProxy Client(string connectionId) => new NonInvokingSingleClientProxy(((IHubCallerClients<IClientProxy>)this).//...
+ new ISingleClientProxy Caller => new NonInvokingSingleClientProxy(((IHubCallerClients<IClientProxy>)this).//...
}
Usage Examples
public class MyHub : Hub
{
public async Task BroadcastCallerResult()
{
var num = await Clients.Caller.InvokeAsync<int>("GetNum");
await Clients.Others.SendAsync("BroadcastNum", num);
}
}
Alternative Designs
- Keep the existing
Single(connectionId)
method and addnew Caller
andnew Client
. - Leave it as is with just
Single(connectionId)
being the only way to get client return results.
Risks
- This is within reason. Even before, if someone had a custom interface that implemented
IHubClients
and added avoid Single(string whatever)
, that would be source breaking. I don't think anyone has done this though. I also don't think anyone is doing anything like the following which would still technically be source breaking:
public class MyHub : Hub
{
public void SourceBreakingMethod()
{
var selectedProxy = Clients.Caller;
selectedProxy = Clients.All; // <--- Cannot implicitly convert type 'Microsoft.AspNetCore.SignalR.IClientProxy' to 'Microsoft.AspNetCore.SignalR.ISingleClientProxy'.
}
}
But that involves using the returned type of Caller
or Client(connectionId)
to infer a non-covariant type. The above example is the easiest way I can think of doing this, but I don't think anyone does this in practice.