diff --git a/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs b/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs
index 3c9a824b2ba5..84c7df734405 100644
--- a/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs
+++ b/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs
@@ -26,6 +26,7 @@ public partial class HubConnection
public static readonly System.TimeSpan DefaultServerTimeout;
public HubConnection(Microsoft.AspNetCore.SignalR.Client.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
public HubConnection(Microsoft.AspNetCore.SignalR.Client.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
+ public string ConnectionId { get { throw null; } }
public System.TimeSpan HandshakeTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public System.TimeSpan KeepAliveInterval { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public System.TimeSpan ServerTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
diff --git a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs
index cde0dea2a96e..c80966573080 100644
--- a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs
+++ b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs
@@ -54,6 +54,7 @@ public partial class HubConnection
private long _nextActivationSendPing;
private bool _disposed;
private bool _hasInherentKeepAlive;
+ private string _connectionId;
private CancellationToken _uploadStreamToken;
@@ -116,6 +117,13 @@ public partial class HubConnection
///
public TimeSpan HandshakeTimeout { get; set; } = DefaultHandshakeTimeout;
+ ///
+ /// Gets the connection's current Id. This value will be cleared when the connection is stopped and will have a new value every time the connection is (re)established.
+ /// This value will be null if the negotiation step is skipped via HttpConnectionOptions or if the WebSockets transport is explicitly specified because the
+ /// client skips negotiation in that case as well.
+ ///
+ public string ConnectionId => _connectionId;
+
///
/// Indicates the state of the to the server.
///
@@ -338,6 +346,7 @@ private async Task StartAsyncCore(CancellationToken cancellationToken)
// Start the connection
var connection = await _connectionFactory.ConnectAsync(_protocol.TransferFormat);
+ _connectionId = connection.ConnectionId;
var startingConnectionState = new ConnectionState(connection, this);
_hasInherentKeepAlive = connection.Features.Get()?.HasInherentKeepAlive ?? false;
@@ -405,6 +414,8 @@ private async Task StopAsyncCore(bool disposing)
(_serviceProvider as IDisposable)?.Dispose();
_disposed = true;
}
+
+ _connectionId = null;
}
finally
{
diff --git a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs
index 1d403d4609a2..9ffe7b3eb99e 100644
--- a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs
+++ b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs
@@ -155,6 +155,41 @@ public async Task CanStopAndStartConnection(string protocolName, HttpTransportTy
}
}
+ [Theory]
+ [MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))]
+ [LogLevel(LogLevel.Trace)]
+ public async Task CanAccessConnectionIdFromHubConnection(string protocolName, HttpTransportType transportType, string path)
+ {
+ var protocol = HubProtocols[protocolName];
+ using (StartServer(out var server))
+ {
+ var connection = CreateHubConnection(server.Url, path, transportType, protocol, LoggerFactory);
+ try
+ {
+ Assert.Null(connection.ConnectionId);
+ await connection.StartAsync().OrTimeout();
+ var originalClientConnectionId = connection.ConnectionId;
+ var connectionIdFromServer = await connection.InvokeAsync(nameof(TestHub.GetCallerConnectionId)).OrTimeout();
+ Assert.Equal(connection.ConnectionId, connectionIdFromServer);
+ await connection.StopAsync().OrTimeout();
+ Assert.Null(connection.ConnectionId);
+ await connection.StartAsync().OrTimeout();
+ connectionIdFromServer = await connection.InvokeAsync(nameof(TestHub.GetCallerConnectionId)).OrTimeout();
+ Assert.NotEqual(originalClientConnectionId, connectionIdFromServer);
+ Assert.Equal(connection.ConnectionId, connectionIdFromServer);
+ }
+ catch (Exception ex)
+ {
+ LoggerFactory.CreateLogger().LogError(ex, "{ExceptionType} from test", ex.GetType().FullName);
+ throw;
+ }
+ finally
+ {
+ await connection.DisposeAsync().OrTimeout();
+ }
+ }
+ }
+
[Theory]
[MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))]
[LogLevel(LogLevel.Trace)]
diff --git a/src/SignalR/clients/csharp/Client/test/FunctionalTests/Hubs.cs b/src/SignalR/clients/csharp/Client/test/FunctionalTests/Hubs.cs
index af321348b5d8..53efb39162dd 100644
--- a/src/SignalR/clients/csharp/Client/test/FunctionalTests/Hubs.cs
+++ b/src/SignalR/clients/csharp/Client/test/FunctionalTests/Hubs.cs
@@ -36,6 +36,11 @@ public async Task CallHandlerThatDoesntExist()
await Clients.Client(Context.ConnectionId).SendAsync("NoClientHandler");
}
+ public string GetCallerConnectionId()
+ {
+ return Context.ConnectionId;
+ }
+
public ChannelReader StreamEcho(ChannelReader source) => TestHubMethodsImpl.StreamEcho(source);
public string GetUserIdentifier()
@@ -110,6 +115,11 @@ public async Task CallHandlerThatDoesntExist()
await Clients.Client(Context.ConnectionId).NoClientHandler();
}
+ public string GetCallerConnectionId()
+ {
+ return Context.ConnectionId;
+ }
+
public ChannelReader StreamEcho(ChannelReader source) => TestHubMethodsImpl.StreamEcho(source);
}
@@ -135,6 +145,11 @@ public async Task CallHandlerThatDoesntExist()
await Clients.Client(Context.ConnectionId).NoClientHandler();
}
+ public string GetCallerConnectionId()
+ {
+ return Context.ConnectionId;
+ }
+
public ChannelReader StreamEcho(ChannelReader source) => TestHubMethodsImpl.StreamEcho(source);
}