使用 SignalR .NET 7 中的新客户端结果功能时,我遇到一个错误:不允许在 OnConnectedAsync Hub 方法中使用客户端结果。
我的用例是我有多个客户端,我使用OnConnectedAsync 方法缓存每个客户端的连接 ID,以便我可以向特定客户端发送消息并等待他们的响应。
这是我的集线器类,请注意,当我调用客户端方法时抛出异常:
return await Clients.Client(connectionMetadata.ConnectionId).InvokeAsync<bool>("CreateConnection", message, CancellationToken.None);
public class ConnectionCreationHub : Hub
{
private readonly List<ConnectionMetadata> connectionIdsCache;
private readonly ITokenValidator2 validator;
public ConnectionCreationHub(
ITokenValidator2 validator,
List<ConnectionMetadata> connectionIdsCache)
{
this.validator = validator;
this.connectionIdsCache = connectionIdsCache;
}
public async Task<bool> SendCreateConnectionMessage(
Guid userId,
string message)
{
var connectionMetadata =
connectionIdsCache
.FirstOrDefault(entry => entry.UserId.Equals(userId));
if (connectionMetadata == null)
{
throw new Exception($"Client with userId {userId} not connected");
}
//The following line throws an error "System.InvalidOperationException: Client results inside OnConnectedAsync Hub methods are not allowed."
return await Clients.Client(connectionMetadata.ConnectionId).InvokeAsync<bool>("CreateConnection", message, CancellationToken.None);
}
public override async Task OnConnectedAsync()
{
//Validate authorization token, and cache connection ID.
var tokenClaims = await this.AuthorizeOrThrow(validator);
var userId = tokenClaims.UserId;
connectionIdsCache.Add(new(userId, Context.ConnectionId));
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
//Clean cache on client disconnected
var connectionMetadata =
connectionIdsCache
.FirstOrDefault(entry => entry.ConnectionId == Context.ConnectionId);
if (connectionMetadata != null)
{
connectionIdsCache.Remove(connectionMetadata);
}
await base.OnDisconnectedAsync(exception);
}
}
我希望我的客户会收到该消息,但我收到了上述错误。请注意,只有使用 InvokeAsync 时才会发生这种情况。 *SendAsync 正在按预期工作。
以下是异常详细信息:
System.InvalidOperationException
HResult=0x80131509
Message=Client results inside OnConnectedAsync Hub methods are not allowed.
Source=Microsoft.AspNetCore.SignalR.Core
StackTrace:
at Microsoft.AspNetCore.SignalR.Internal.HubCallerClients.NoInvokeSingleClientProxy.InvokeCoreAsync[T](String method, Object[] args, CancellationToken cancellationToken)
at Microsoft.AspNetCore.SignalR.ClientProxyExtensions.InvokeAsync[T](ISingleClientProxy clientProxy, String method, Object arg1, CancellationToken cancellationToken)
at ConnectionCreationHub.<SendCreateConnectionMessage>d__3.MoveNext() in *\ConnectionCreationHub.cs:line 42
已知问题:使用客户端结果时从 OnConnected 和 OnDisconnected 抛出
优化后的代码如下,大家可以参考一下。
public async Task<bool> SendCreateConnectionMessage(
Guid userId,
string message)
{
var connectionMetadata =
connectionIdsCache
.FirstOrDefault(entry => entry.UserId.Equals(userId));
if (connectionMetadata == null)
{
throw new Exception($"Client with userId {userId} not connected");
}
//We need to ensure that this invocation is not in the lifecycle context.
bool result = false;
try
{
result = await Clients.Client(connectionMetadata.ConnectionId)
.InvokeAsync<bool>("CreateConnection", message, CancellationToken.None);
}
catch (Exception ex)
{
// Handle the exception.
throw new Exception($"Error while trying to invoke client method: {ex.Message}", ex);
}
return result;
}