Search code examples
c#asp.net-core.net-coresignalrclaims

How to iterate over users in asp.net core SignalR?


In a SignalR application on .net core, I have a working piece of code similar to the following:

public Task SendPrivateMessage(string user, string message)
{
    var sender = Context.User.Claims.FirstOrDefault(cl => cl.Type == "username")?.Value;
    return Clients.User(user)
                  .SendAsync("ReceiveMessage", $"Message from {sender}: {message}");
}

This sends a message from the currently connected user, to the specified user.

Now I'd like to send an individual message to every user connected; something like the following concept:

public Task UpdateMessageStatus()
{
    foreach(client in Clients.Users)
    {
         var nrOfMessages = GetMessageCount();
         client.SendAsync($"You have {nrOfMessages} messages.");
    }
}

How can I achieve this; i.e. how can I iterate over connected users, and send individual messages to each of them?

Edit / clarification

As mentioned, I needed to send individual (i.e. specialized) messages to each connected user, so using Clients.All was not an option, as that can only be used to send the same message to all connected users.

I ended up doing something similar to what Mark C. posted in the accepted answer.


Solution

  • If you're deadset on not using the Clients.All API that is given to you, you can keep a local cache of the connected users and use that as your list of connected users.

    Something like this :

    static HashSet<string> CurrentConnections = new HashSet<string>();
    
        public override Task OnConnected()
        {
            var id = Context.ConnectionId;
            CurrentConnections.Add(id);
    
            return base.OnConnected();
        }
    
        public Task SendAllClientsMessage()
        {
            foreach (var activeConnection in GetAllActiveConnections())
            {
                Clients.Client(activeConnection).SendMessageAsync("Hi");
            }
        }
    
        public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
        {
            var connection = CurrentConnections.FirstOrDefault(x => x == Context.ConnectionId);
    
            if (connection != null)
            {
                CurrentConnections.Remove(connection);
            }
    
            return base.OnDisconnected(stopCalled);
        }
    
    
        //return list of all active connections
        public List<string> GetAllActiveConnections()
        {
            return CurrentConnections.ToList();
        }