Skip to content

Commit 3f62031

Browse files
Make HubConnection methods virtual (#35132)
1 parent 5e6f3a4 commit 3f62031

File tree

3 files changed

+63
-19
lines changed

3 files changed

+63
-19
lines changed

src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ public HubConnection(IConnectionFactory connectionFactory,
233233
/// </summary>
234234
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None" />.</param>
235235
/// <returns>A <see cref="Task"/> that represents the asynchronous start.</returns>
236-
public async Task StartAsync(CancellationToken cancellationToken = default)
236+
public virtual async Task StartAsync(CancellationToken cancellationToken = default)
237237
{
238238
CheckDisposed();
239239
using (_logger.BeginScope(_logScope))
@@ -286,7 +286,7 @@ private async Task StartAsyncInner(CancellationToken cancellationToken = default
286286
/// </summary>
287287
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None" />.</param>
288288
/// <returns>A <see cref="Task"/> that represents the asynchronous stop.</returns>
289-
public async Task StopAsync(CancellationToken cancellationToken = default)
289+
public virtual async Task StopAsync(CancellationToken cancellationToken = default)
290290
{
291291
CheckDisposed();
292292
using (_logger.BeginScope(_logScope))
@@ -301,7 +301,7 @@ public async Task StopAsync(CancellationToken cancellationToken = default)
301301
/// Disposes the <see cref="HubConnection"/>.
302302
/// </summary>
303303
/// <returns>A <see cref="ValueTask"/> that represents the asynchronous dispose.</returns>
304-
public async ValueTask DisposeAsync()
304+
public virtual async ValueTask DisposeAsync()
305305
{
306306
if (!_disposed)
307307
{
@@ -324,7 +324,7 @@ public async ValueTask DisposeAsync()
324324
/// <remarks>
325325
/// This is a low level method for registering a handler. Using an <see cref="HubConnectionExtensions"/> <c>On</c> extension method is recommended.
326326
/// </remarks>
327-
public IDisposable On(string methodName, Type[] parameterTypes, Func<object?[], object, Task> handler, object state)
327+
public virtual IDisposable On(string methodName, Type[] parameterTypes, Func<object?[], object, Task> handler, object state)
328328
{
329329
Log.RegisteringHandler(_logger, methodName);
330330

@@ -349,7 +349,7 @@ public IDisposable On(string methodName, Type[] parameterTypes, Func<object?[],
349349
/// Removes all handlers associated with the method with the specified method name.
350350
/// </summary>
351351
/// <param name="methodName">The name of the hub method from which handlers are being removed</param>
352-
public void Remove(string methodName)
352+
public virtual void Remove(string methodName)
353353
{
354354
CheckDisposed();
355355
Log.RemovingHandlers(_logger, methodName);
@@ -370,7 +370,7 @@ public void Remove(string methodName)
370370
/// <remarks>
371371
/// This is a low level method for invoking a streaming hub method on the server. Using an <see cref="HubConnectionExtensions"/> <c>StreamAsChannelAsync</c> extension method is recommended.
372372
/// </remarks>
373-
public async Task<ChannelReader<object?>> StreamAsChannelCoreAsync(string methodName, Type returnType, object?[] args, CancellationToken cancellationToken = default)
373+
public virtual async Task<ChannelReader<object?>> StreamAsChannelCoreAsync(string methodName, Type returnType, object?[] args, CancellationToken cancellationToken = default)
374374
{
375375
using (_logger.BeginScope(_logScope))
376376
{
@@ -392,7 +392,7 @@ public void Remove(string methodName)
392392
/// <remarks>
393393
/// This is a low level method for invoking a hub method on the server. Using an <see cref="HubConnectionExtensions"/> <c>InvokeAsync</c> extension method is recommended.
394394
/// </remarks>
395-
public async Task<object?> InvokeCoreAsync(string methodName, Type returnType, object?[] args, CancellationToken cancellationToken = default)
395+
public virtual async Task<object?> InvokeCoreAsync(string methodName, Type returnType, object?[] args, CancellationToken cancellationToken = default)
396396
{
397397
using (_logger.BeginScope(_logScope))
398398
{
@@ -411,7 +411,7 @@ public void Remove(string methodName)
411411
/// <remarks>
412412
/// This is a low level method for invoking a hub method on the server. Using an <see cref="HubConnectionExtensions"/> <c>SendAsync</c> extension method is recommended.
413413
/// </remarks>
414-
public async Task SendCoreAsync(string methodName, object?[] args, CancellationToken cancellationToken = default)
414+
public virtual async Task SendCoreAsync(string methodName, object?[] args, CancellationToken cancellationToken = default)
415415
{
416416
using (_logger.BeginScope(_logScope))
417417
{
@@ -551,7 +551,7 @@ private async Task StopAsyncCore(bool disposing)
551551
/// <returns>
552552
/// A <see cref="IAsyncEnumerable{TResult}"/> that represents the stream.
553553
/// </returns>
554-
public IAsyncEnumerable<TResult> StreamAsyncCore<TResult>(string methodName, object?[] args, CancellationToken cancellationToken = default)
554+
public virtual IAsyncEnumerable<TResult> StreamAsyncCore<TResult>(string methodName, object?[] args, CancellationToken cancellationToken = default)
555555
{
556556
var cts = cancellationToken.CanBeCanceled ? CancellationTokenSource.CreateLinkedTokenSource(cancellationToken) : new CancellationTokenSource();
557557
var stream = CastIAsyncEnumerable<TResult>(methodName, args, cts);

src/SignalR/clients/csharp/Client.Core/src/PublicAPI.Unshipped.txt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
*REMOVED*~Microsoft.AspNetCore.SignalR.Client.HubConnection.StopAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
1010
*REMOVED*~Microsoft.AspNetCore.SignalR.Client.HubConnection.On(string methodName, System.Type[] parameterTypes, System.Func<object[], object, System.Threading.Tasks.Task> handler, object state) -> System.IDisposable
1111
*REMOVED*~Microsoft.AspNetCore.SignalR.Client.HubConnection.Remove(string methodName) -> void
12+
*REMOVED*Microsoft.AspNetCore.SignalR.Client.HubConnection.DisposeAsync() -> System.Threading.Tasks.ValueTask
1213
*REMOVED*~Microsoft.AspNetCore.SignalR.Client.HubConnection.StreamAsChannelCoreAsync(string methodName, System.Type returnType, object[] args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Threading.Channels.ChannelReader<object>>
13-
*REMOVED*Microsoft.AspNetCore.SignalR.Client.HubConnection.InvokeCoreAsync(string! methodName, System.Type! returnType, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<object?>!
14-
*REMOVED*Microsoft.AspNetCore.SignalR.Client.HubConnection.SendCoreAsync(string! methodName, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
14+
*REMOVED*~Microsoft.AspNetCore.SignalR.Client.HubConnection.InvokeCoreAsync(string methodName, System.Type returnType, object[] args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<object>
15+
*REMOVED*~Microsoft.AspNetCore.SignalR.Client.HubConnection.SendCoreAsync(string methodName, object[] args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
1516
*REMOVED*~Microsoft.AspNetCore.SignalR.Client.HubConnection.StreamAsyncCore<TResult>(string methodName, object[] args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IAsyncEnumerable<TResult>
1617
*REMOVED*~static Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.On(this Microsoft.AspNetCore.SignalR.Client.HubConnection hubConnection, string methodName, System.Action handler) -> System.IDisposable
1718
*REMOVED*~static Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.On(this Microsoft.AspNetCore.SignalR.Client.HubConnection hubConnection, string methodName, System.Func<System.Threading.Tasks.Task> handler) -> System.IDisposable
@@ -109,14 +110,6 @@ Microsoft.AspNetCore.SignalR.Client.HubConnection.Reconnected -> System.Func<str
109110
Microsoft.AspNetCore.SignalR.Client.HubConnection.ConnectionId.get -> string?
110111
Microsoft.AspNetCore.SignalR.Client.HubConnection.HubConnection(Microsoft.AspNetCore.Connections.IConnectionFactory! connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol! protocol, System.Net.EndPoint! endPoint, System.IServiceProvider! serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory) -> void
111112
Microsoft.AspNetCore.SignalR.Client.HubConnection.HubConnection(Microsoft.AspNetCore.Connections.IConnectionFactory! connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol! protocol, System.Net.EndPoint! endPoint, System.IServiceProvider! serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory, Microsoft.AspNetCore.SignalR.Client.IRetryPolicy! reconnectPolicy) -> void
112-
Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
113-
Microsoft.AspNetCore.SignalR.Client.HubConnection.StopAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
114-
Microsoft.AspNetCore.SignalR.Client.HubConnection.On(string! methodName, System.Type![]! parameterTypes, System.Func<object?[]!, object!, System.Threading.Tasks.Task!>! handler, object! state) -> System.IDisposable!
115-
Microsoft.AspNetCore.SignalR.Client.HubConnection.Remove(string! methodName) -> void
116-
Microsoft.AspNetCore.SignalR.Client.HubConnection.StreamAsChannelCoreAsync(string! methodName, System.Type! returnType, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Threading.Channels.ChannelReader<object?>!>!
117-
Microsoft.AspNetCore.SignalR.Client.HubConnection.InvokeCoreAsync(string! methodName, System.Type! returnType, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<object?>!
118-
Microsoft.AspNetCore.SignalR.Client.HubConnection.SendCoreAsync(string! methodName, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
119-
Microsoft.AspNetCore.SignalR.Client.HubConnection.StreamAsyncCore<TResult>(string! methodName, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IAsyncEnumerable<TResult>!
120113
static Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.On(this Microsoft.AspNetCore.SignalR.Client.HubConnection! hubConnection, string! methodName, System.Action! handler) -> System.IDisposable!
121114
static Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.On(this Microsoft.AspNetCore.SignalR.Client.HubConnection! hubConnection, string! methodName, System.Func<System.Threading.Tasks.Task!>! handler) -> System.IDisposable!
122115
static Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.On(this Microsoft.AspNetCore.SignalR.Client.HubConnection! hubConnection, string! methodName, System.Type![]! parameterTypes, System.Func<object?[]!, System.Threading.Tasks.Task!>! handler) -> System.IDisposable!
@@ -207,3 +200,12 @@ static Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.StreamAsync<T
207200
static Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.StreamAsync<TResult>(this Microsoft.AspNetCore.SignalR.Client.HubConnection! hubConnection, string! methodName, object? arg1, object? arg2, object? arg3, object? arg4, object? arg5, object? arg6, object? arg7, object? arg8, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IAsyncEnumerable<TResult>!
208201
static Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.StreamAsync<TResult>(this Microsoft.AspNetCore.SignalR.Client.HubConnection! hubConnection, string! methodName, object? arg1, object? arg2, object? arg3, object? arg4, object? arg5, object? arg6, object? arg7, object? arg8, object? arg9, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IAsyncEnumerable<TResult>!
209202
static Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.StreamAsync<TResult>(this Microsoft.AspNetCore.SignalR.Client.HubConnection! hubConnection, string! methodName, object? arg1, object? arg2, object? arg3, object? arg4, object? arg5, object? arg6, object? arg7, object? arg8, object? arg9, object? arg10, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IAsyncEnumerable<TResult>!
203+
virtual Microsoft.AspNetCore.SignalR.Client.HubConnection.DisposeAsync() -> System.Threading.Tasks.ValueTask
204+
virtual Microsoft.AspNetCore.SignalR.Client.HubConnection.InvokeCoreAsync(string! methodName, System.Type! returnType, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<object?>!
205+
virtual Microsoft.AspNetCore.SignalR.Client.HubConnection.On(string! methodName, System.Type![]! parameterTypes, System.Func<object?[]!, object!, System.Threading.Tasks.Task!>! handler, object! state) -> System.IDisposable!
206+
virtual Microsoft.AspNetCore.SignalR.Client.HubConnection.Remove(string! methodName) -> void
207+
virtual Microsoft.AspNetCore.SignalR.Client.HubConnection.SendCoreAsync(string! methodName, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
208+
virtual Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
209+
virtual Microsoft.AspNetCore.SignalR.Client.HubConnection.StopAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
210+
virtual Microsoft.AspNetCore.SignalR.Client.HubConnection.StreamAsChannelCoreAsync(string! methodName, System.Type! returnType, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Threading.Channels.ChannelReader<object?>!>!
211+
virtual Microsoft.AspNetCore.SignalR.Client.HubConnection.StreamAsyncCore<TResult>(string! methodName, object?[]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IAsyncEnumerable<TResult>!

src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Buffers;
66
using System.IO;
7+
using System.Net;
78
using System.Net.WebSockets;
89
using System.Threading;
910
using System.Threading.Channels;
@@ -713,6 +714,47 @@ public async Task VerifyUserOptionsAreNotChanged()
713714
}
714715
}
715716

717+
[Fact]
718+
public async Task HubConnectionIsMockable()
719+
{
720+
var mockConnection = new Mock<HubConnection>(new Mock<IConnectionFactory>().Object, new Mock<IHubProtocol>().Object, new Mock<EndPoint>().Object,
721+
new Mock<IServiceProvider>().Object, new Mock<ILoggerFactory>().Object, new Mock<IRetryPolicy>().Object);
722+
723+
mockConnection.Setup(c => c.StartAsync(default)).Returns(() => Task.CompletedTask);
724+
mockConnection.Setup(c => c.StopAsync(default)).Returns(() => Task.CompletedTask);
725+
mockConnection.Setup(c => c.DisposeAsync()).Returns(() => ValueTask.CompletedTask);
726+
mockConnection.Setup(c => c.On(It.IsAny<string>(), It.IsAny<Type[]>(), It.IsAny<Func<object[], object, Task>>(), It.IsAny<object>()));
727+
mockConnection.Setup(c => c.Remove(It.IsAny<string>()));
728+
mockConnection.Setup(c => c.StreamAsChannelCoreAsync(It.IsAny<string>(), It.IsAny<Type>(), It.IsAny<object[]>(), It.IsAny<CancellationToken>()))
729+
.Returns(() => Task.FromResult(It.IsAny<ChannelReader<object>>()));
730+
mockConnection.Setup(c => c.InvokeCoreAsync(It.IsAny<string>(), It.IsAny<Type>(), It.IsAny<object[]>(), It.IsAny<CancellationToken>())).Returns(() => Task.FromResult(It.IsAny<object>()));
731+
mockConnection.Setup(c => c.SendCoreAsync(It.IsAny<string>(), It.IsAny<object[]>(), It.IsAny<CancellationToken>())).Returns(() => Task.CompletedTask);
732+
mockConnection.Setup(c => c.StreamAsyncCore<object>(It.IsAny<string>(), It.IsAny<object[]>(), It.IsAny<CancellationToken>())).Returns(() => It.IsAny<IAsyncEnumerable<object>>());
733+
734+
var hubConnection = mockConnection.Object;
735+
// .On extension method
736+
_ = hubConnection.On("someMethod", () => { });
737+
// .On non-extension method
738+
_ = hubConnection.On("someMethod2", new Type[] { typeof(int) }, (args, obj) => Task.CompletedTask, 2);
739+
hubConnection.Remove("someMethod");
740+
await hubConnection.StartAsync();
741+
_ = await hubConnection.StreamAsChannelCoreAsync("stream", typeof(int), Array.Empty<object>(), default);
742+
_ = await hubConnection.InvokeCoreAsync("test", typeof(int), Array.Empty<object>(), default);
743+
await hubConnection.SendCoreAsync("test2", Array.Empty<object>(), default);
744+
_ = hubConnection.StreamAsyncCore<int>("stream2", Array.Empty<object>(), default);
745+
await hubConnection.StopAsync();
746+
747+
mockConnection.Verify(c => c.On("someMethod", It.IsAny<Type[]>(), It.IsAny<Func<object[], object, Task>>(), It.IsAny<object>()), Times.Once);
748+
mockConnection.Verify(c => c.On("someMethod2", It.IsAny<Type[]>(), It.IsAny<Func<object[], object, Task>>(), 2), Times.Once);
749+
mockConnection.Verify(c => c.Remove("someMethod"), Times.Once);
750+
mockConnection.Verify(c => c.StreamAsChannelCoreAsync("stream", It.IsAny<Type>(), It.IsAny<object[]>(), It.IsAny<CancellationToken>()), Times.Once);
751+
mockConnection.Verify(c => c.InvokeCoreAsync("test", typeof(int), Array.Empty<object>(), It.IsAny<CancellationToken>()), Times.Once);
752+
mockConnection.Verify(c => c.SendCoreAsync("test2", Array.Empty<object>(), It.IsAny<CancellationToken>()), Times.Once);
753+
mockConnection.Verify(c => c.StreamAsyncCore<int>("stream2", Array.Empty<object>(), It.IsAny<CancellationToken>()), Times.Once);
754+
mockConnection.Verify(c => c.StartAsync(It.IsAny<CancellationToken>()), Times.Once);
755+
mockConnection.Verify(c => c.StopAsync(It.IsAny<CancellationToken>()), Times.Once);
756+
}
757+
716758
private class SampleObject
717759
{
718760
public SampleObject(string foo, int bar)

0 commit comments

Comments
 (0)