I have a sample demo about this requirement and ConcurrentDictionary
is thread-safe. Hope the code snippet below useful to you.
Test Result

Program.cs
...
builder.Services.AddSingleton<MainHub>();
builder.Services.AddSignalR().AddNewtonsoftJsonProtocol(opts => opts.PayloadSerializerSettings.TypeNameHandling = TypeNameHandling.Auto);
...
Method inside Hub Class, MainHub.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Connections.Features;
using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;
using System.Diagnostics;
namespace SignalRMiddleawre.Hubs
{
//[Authorize]
public partial class MainHub : Hub
{
public static ConcurrentDictionary<string?, List<string>>? ConnectedUsers;
public MainHub()
{
ConnectedUsers = new ConcurrentDictionary<string?, List<string>>();
}
public override async Task OnConnectedAsync()
{
//// Get HttpContext In asp.net core signalr
//IHttpContextFeature hcf = (IHttpContextFeature)this.Context.Features[typeof(IHttpContextFeature)];
//HttpContext hc = hcf.HttpContext;
//string uid = hc.Request.Path.Value.ToString().Split(new string[] { "/", "" }, StringSplitOptions.RemoveEmptyEntries)[1].ToString();
string? userid = Context.User?.Identity?.Name;
if (userid == null || userid.Equals(string.Empty))
{
Trace.TraceInformation("user not loged in, can't connect signalr service");
return;
}
Trace.TraceInformation(userid + "connected");
// save connection
List<string>? existUserConnectionIds;
ConnectedUsers.TryGetValue(userid, out existUserConnectionIds);
if (existUserConnectionIds == null)
{
existUserConnectionIds = new List<string>();
}
existUserConnectionIds.Add(Context.ConnectionId);
ConnectedUsers.TryAdd(userid, existUserConnectionIds);
//await Clients.All.SendAsync("ServerInfo", userid, userid + " connected, connectionId = " + Context.ConnectionId);
await base.OnConnectedAsync();
}
public async Task ListConnectedUsers()
{
List<string?> data = ConnectedUsers.Keys.ToList();
await Clients.All.SendAsync("ListConnectedUsers", data);
}
}
}
Client Side Code
...
connection.on("ListConnectedUsers", function (data) {
var li = document.createElement("li");
document.getElementById("messagesList").appendChild(li);
li.textContent = `System says : Current Users :` + data;
});
Test Api Code
using AspCore7_Web_Identity.Areas.Identity.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using OfficeOpenXml;
using SignalRMiddleawre.Hubs;
using System.Net.Http;
using System.Web.Services.Description;
namespace AspCore7_Web_Identity.Controllers
{
public class TestController : Controller
{
private readonly ILogger<TestController> _logger;
private readonly IHubContext<MainHub> _hubContext;
private readonly MainHub _hub;
public TestController(ILogger<TestController> logger, IHubContext<MainHub> hubcontext, MainHub hub)
{
_logger = logger;
_hubContext = hubcontext;
_hub = hub;
}
public IActionResult CheckConnectedUsers()
{
_hub.ListConnectedUsers();
return Ok("Success");
}
}
}