Skip to content

Commit d4fccce

Browse files
authored
[SignalR] Replace Single(connectionId) with Client(connectionId) and Caller (#42236)
1 parent 9ac541b commit d4fccce

File tree

12 files changed

+96
-73
lines changed

12 files changed

+96
-73
lines changed

src/Components/Server/test/Circuits/ComponentHubTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public async Task CannotInvokeOnLocationChangedBeforeInitialization()
8585
mockClientProxy.Verify(m => m.SendCoreAsync("JS.Error", new[] { errorMessage }, It.IsAny<CancellationToken>()), Times.Once());
8686
}
8787

88-
private static (Mock<IClientProxy>, ComponentHub) InitializeComponentHub()
88+
private static (Mock<ISingleClientProxy>, ComponentHub) InitializeComponentHub()
8989
{
9090
var ephemeralDataProtectionProvider = new EphemeralDataProtectionProvider();
9191
var circuitIdFactory = new CircuitIdFactory(ephemeralDataProtectionProvider);
@@ -112,7 +112,7 @@ private static (Mock<IClientProxy>, ComponentHub) InitializeComponentHub()
112112
// Here we mock out elements of the Hub that are typically configured
113113
// by SignalR as clients connect to the hub.
114114
var mockCaller = new Mock<IHubCallerClients>();
115-
var mockClientProxy = new Mock<IClientProxy>();
115+
var mockClientProxy = new Mock<ISingleClientProxy>();
116116
mockCaller.Setup(x => x.Caller).Returns(mockClientProxy.Object);
117117
hub.Clients = mockCaller.Object;
118118
var mockContext = new Mock<HubCallerContext>();

src/SignalR/clients/ts/FunctionalTests/Startup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<
232232
{
233233
try
234234
{
235-
var result = await hubContext.Clients.Single(id).InvokeAsync<int>("Result");
235+
var result = await hubContext.Clients.Client(id).InvokeAsync<int>("Result");
236236
return result.ToString(CultureInfo.InvariantCulture);
237237
}
238238
catch (Exception ex)

src/SignalR/server/Core/src/IHubCallerClients.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using Microsoft.AspNetCore.SignalR.Internal;
5+
46
namespace Microsoft.AspNetCore.SignalR;
57

68
/// <summary>
@@ -13,5 +15,11 @@ public interface IHubCallerClients : IHubCallerClients<IClientProxy>
1315
/// </summary>
1416
/// <param name="connectionId">The connection ID.</param>
1517
/// <returns>A client caller.</returns>
16-
new ISingleClientProxy Single(string connectionId) => throw new NotImplementedException();
18+
new ISingleClientProxy Client(string connectionId) => new NonInvokingSingleClientProxy(((IHubCallerClients<IClientProxy>)this).Client(connectionId), "IHubCallerClients.Client(string connectionId)");
19+
20+
/// <summary>
21+
/// Gets a proxy that can be used to invoke methods on the calling client and receive results.
22+
/// </summary>
23+
/// <returns>A client caller.</returns>
24+
new ISingleClientProxy Caller => new NonInvokingSingleClientProxy(((IHubCallerClients<IClientProxy>)this).Caller, "IHubCallerClients.Caller");
1725
}

src/SignalR/server/Core/src/IHubClients.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using Microsoft.AspNetCore.SignalR.Internal;
5+
46
namespace Microsoft.AspNetCore.SignalR;
57

68
/// <summary>
@@ -13,5 +15,5 @@ public interface IHubClients : IHubClients<IClientProxy>
1315
/// </summary>
1416
/// <param name="connectionId">The connection ID.</param>
1517
/// <returns>A client caller.</returns>
16-
new ISingleClientProxy Single(string connectionId) => throw new NotImplementedException();
18+
new ISingleClientProxy Client(string connectionId) => new NonInvokingSingleClientProxy(((IHubClients<IClientProxy>)this).Client(connectionId), "IHubClients.Client(string connectionId)");
1719
}

src/SignalR/server/Core/src/IHubClients`T.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,6 @@ namespace Microsoft.AspNetCore.SignalR;
99
/// <typeparam name="T">The client invoker type.</typeparam>
1010
public interface IHubClients<T>
1111
{
12-
/// <summary>
13-
/// Gets a <typeparamref name="T" /> that can be used to invoke methods on a single client connected to the hub and receive results.
14-
/// </summary>
15-
/// <param name="connectionId">The connection ID.</param>
16-
/// <returns>A client caller.</returns>
17-
T Single(string connectionId) => Client(connectionId);
18-
1912
/// <summary>
2013
/// Gets a <typeparamref name="T" /> that can be used to invoke methods on all clients connected to the hub.
2114
/// </summary>

src/SignalR/server/Core/src/Internal/HubCallerClients.cs

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,37 +18,19 @@ public HubCallerClients(IHubClients hubClients, string connectionId, bool parall
1818
_parallelEnabled = parallelEnabled;
1919
}
2020

21-
private sealed class NotParallelSingleClientProxy : ISingleClientProxy
21+
IClientProxy IHubCallerClients<IClientProxy>.Caller => Caller;
22+
public ISingleClientProxy Caller
2223
{
23-
private readonly ISingleClientProxy _proxy;
24-
25-
public NotParallelSingleClientProxy(ISingleClientProxy hubClients)
26-
{
27-
_proxy = hubClients;
28-
}
29-
30-
public Task<T> InvokeCoreAsync<T>(string method, object?[] args, CancellationToken cancellationToken = default)
24+
get
3125
{
32-
throw new InvalidOperationException("Client results inside a Hub method requires HubOptions.MaximumParallelInvocationsPerClient to be greater than 1.");
33-
}
34-
35-
public Task SendCoreAsync(string method, object?[] args, CancellationToken cancellationToken = default)
36-
{
37-
return _proxy.SendCoreAsync(method, args, cancellationToken);
26+
if (!_parallelEnabled)
27+
{
28+
return new NotParallelSingleClientProxy(_hubClients.Client(_connectionId));
29+
}
30+
return _hubClients.Client(_connectionId);
3831
}
3932
}
4033

41-
public ISingleClientProxy Single(string connectionId)
42-
{
43-
if (!_parallelEnabled)
44-
{
45-
return new NotParallelSingleClientProxy(_hubClients.Single(connectionId));
46-
}
47-
return _hubClients.Single(connectionId);
48-
}
49-
50-
public IClientProxy Caller => _hubClients.Client(_connectionId);
51-
5234
public IClientProxy Others => _hubClients.AllExcept(_currentConnectionId);
5335

5436
public IClientProxy All => _hubClients.All;
@@ -58,8 +40,13 @@ public IClientProxy AllExcept(IReadOnlyList<string> excludedConnectionIds)
5840
return _hubClients.AllExcept(excludedConnectionIds);
5941
}
6042

61-
public IClientProxy Client(string connectionId)
43+
IClientProxy IHubClients<IClientProxy>.Client(string connectionId) => Client(connectionId);
44+
public ISingleClientProxy Client(string connectionId)
6245
{
46+
if (!_parallelEnabled)
47+
{
48+
return new NotParallelSingleClientProxy(_hubClients.Client(connectionId));
49+
}
6350
return _hubClients.Client(connectionId);
6451
}
6552

@@ -97,4 +84,24 @@ public IClientProxy Users(IReadOnlyList<string> userIds)
9784
{
9885
return _hubClients.Users(userIds);
9986
}
87+
88+
private sealed class NotParallelSingleClientProxy : ISingleClientProxy
89+
{
90+
private readonly ISingleClientProxy _proxy;
91+
92+
public NotParallelSingleClientProxy(ISingleClientProxy hubClients)
93+
{
94+
_proxy = hubClients;
95+
}
96+
97+
public Task<T> InvokeCoreAsync<T>(string method, object?[] args, CancellationToken cancellationToken = default)
98+
{
99+
throw new InvalidOperationException("Client results inside a Hub method requires HubOptions.MaximumParallelInvocationsPerClient to be greater than 1.");
100+
}
101+
102+
public Task SendCoreAsync(string method, object?[] args, CancellationToken cancellationToken = default)
103+
{
104+
return _proxy.SendCoreAsync(method, args, cancellationToken);
105+
}
106+
}
100107
}

src/SignalR/server/Core/src/Internal/HubClients.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,15 @@ public HubClients(HubLifetimeManager<THub> lifetimeManager)
1313
All = new AllClientProxy<THub>(_lifetimeManager);
1414
}
1515

16-
public ISingleClientProxy Single(string connectionId)
17-
{
18-
return new SingleClientProxy<THub>(_lifetimeManager, connectionId);
19-
}
20-
2116
public IClientProxy All { get; }
2217

2318
public IClientProxy AllExcept(IReadOnlyList<string> excludedConnectionIds)
2419
{
2520
return new AllClientsExceptProxy<THub>(_lifetimeManager, excludedConnectionIds);
2621
}
2722

28-
public IClientProxy Client(string connectionId)
23+
IClientProxy IHubClients<IClientProxy>.Client(string connectionId) => Client(connectionId);
24+
public ISingleClientProxy Client(string connectionId)
2925
{
3026
return new SingleClientProxy<THub>(_lifetimeManager, connectionId);
3127
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.SignalR.Internal;
5+
6+
internal sealed class NonInvokingSingleClientProxy : ISingleClientProxy
7+
{
8+
private readonly IClientProxy _clientProxy;
9+
private readonly string _memberName;
10+
11+
public NonInvokingSingleClientProxy(IClientProxy clientProxy, string memberName)
12+
{
13+
_clientProxy = clientProxy;
14+
_memberName = memberName;
15+
}
16+
17+
public Task SendCoreAsync(string method, object?[] args, CancellationToken cancellationToken = default) =>
18+
_clientProxy.SendCoreAsync(method, args, cancellationToken);
19+
20+
public Task<T> InvokeCoreAsync<T>(string method, object?[] args, CancellationToken cancellationToken = default) =>
21+
throw new NotImplementedException($"The default implementation of {_memberName} does not support client return results.");
22+
}

src/SignalR/server/Core/src/Internal/TypedHubClients.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public TypedHubClients(IHubCallerClients dynamicContext)
1212
_hubClients = dynamicContext;
1313
}
1414

15-
public T Client(string connectionId) => TypedClientBuilder<T>.Build(_hubClients.Single(connectionId));
15+
public T Client(string connectionId) => TypedClientBuilder<T>.Build(_hubClients.Client(connectionId));
1616

1717
public T All => TypedClientBuilder<T>.Build(_hubClients.All);
1818

src/SignalR/server/Core/src/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#nullable enable
22
Microsoft.AspNetCore.SignalR.HubOptions.DisableImplicitFromServicesParameters.get -> bool
33
Microsoft.AspNetCore.SignalR.HubOptions.DisableImplicitFromServicesParameters.set -> void
4-
Microsoft.AspNetCore.SignalR.IHubCallerClients.Single(string! connectionId) -> Microsoft.AspNetCore.SignalR.ISingleClientProxy!
5-
Microsoft.AspNetCore.SignalR.IHubClients.Single(string! connectionId) -> Microsoft.AspNetCore.SignalR.ISingleClientProxy!
6-
Microsoft.AspNetCore.SignalR.IHubClients<T>.Single(string! connectionId) -> T
4+
Microsoft.AspNetCore.SignalR.IHubCallerClients.Caller.get -> Microsoft.AspNetCore.SignalR.ISingleClientProxy!
5+
Microsoft.AspNetCore.SignalR.IHubCallerClients.Client(string! connectionId) -> Microsoft.AspNetCore.SignalR.ISingleClientProxy!
6+
Microsoft.AspNetCore.SignalR.IHubClients.Client(string! connectionId) -> Microsoft.AspNetCore.SignalR.ISingleClientProxy!
77
Microsoft.AspNetCore.SignalR.ISingleClientProxy
88
Microsoft.AspNetCore.SignalR.ISingleClientProxy.InvokeCoreAsync<T>(string! method, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<T>!
99
override Microsoft.AspNetCore.SignalR.DefaultHubLifetimeManager<THub>.InvokeConnectionAsync<T>(string! connectionId, string! methodName, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<T>!

0 commit comments

Comments
 (0)