Как перебирать пользователей в SignalR ядра asp.net?

В приложении SignalR на ядре .NET у меня есть рабочий фрагмент кода, подобный следующему:

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}");
}

Это отправляет сообщение от текущего подключенного пользователя указанному пользователю.

Теперь я хотел бы отправить отдельное сообщение каждому подключенному пользователю; что-то вроде следующей концепции:

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

Как я могу этого добиться? т.е. как я могу перебирать подключенных пользователей и отправлять каждому из них индивидуальные сообщения?

Редактирование / уточнение

Как уже упоминалось, мне нужно было отправлять отдельные (то есть специализированные) сообщения каждому подключенному пользователю, поэтому использование Clients.All не было вариантом, поскольку его можно использовать только для отправки одного и того же сообщения всем подключенным пользователям.

В итоге я сделал что-то похожее на то, что Марк С. написал в принятом ответе.


person Kjartan    schedule 17.07.2018    source источник
comment
Не могли бы вы объяснить, в чем проблема с текущей версией UpdateMessageStatus?   -  person Spotted    schedule 17.07.2018
comment
В настоящее время нет свойств, доступных для получения подключенных пользователей или идентификаторов подключений, но вы можете сохранить список пользователей, которые в настоящее время подключены к хабу, с помощью событий OnConnected и OnDisconnected. Дополнительная информация здесь   -  person Thangadurai    schedule 17.07.2018


Ответы (3)


Если вы настроены не использовать предоставленный вам Clients.All API, вы можете сохранить локальный кеш подключенных пользователей и использовать его в качестве списка подключенных пользователей.

Что-то вроде этого :

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();
    }
person Mark C.    schedule 17.07.2018
comment
Просто для пояснения, потому что @Mark C. не сказал это явно: ASP.NET (ни основной, ни чистый) хранит для вас коллекцию, содержащую пользователей. Вы должны поддерживать это самостоятельно. (Как показано выше) - person Ole Albers; 17.07.2018
comment
Есть идеи, как применить вышеуказанный подход, но с введенным контекстом концентратора? - person Yahia; 13.12.2018
comment
@Yahia Вам по-прежнему нужно хранить соединения в той или иной форме и использовать это хранилище для получения клиентов, которым вы хотите транслировать. Я не думаю, что здесь много изменений - person Mark C.; 13.12.2018
comment
Ты прав. Я закончил тем, что определил SendAllClientsMessage как статический в хабе и использовал его из контекста хаба. - person Yahia; 14.12.2018
comment
Вы должны использовать потокобезопасную коллекцию - stackoverflow.com/a/18923091/16940 - обычный HashSet БУДЕТ укусить ты когда-нибудь! - person Simon_Weaver; 18.06.2019
comment
Я согласен, используйте ConcurrentBag или любой другой тип коллекции, который соответствует требованиям. - person Mark C.; 18.06.2019

Вы можете использовать Clients.All, чтобы отправлять сообщения всем своим клиентам.

return Clients.All.SendAsync ("ReceiveMessage", $ "Сообщение от {отправителя}: {сообщение}");

person dcy    schedule 17.07.2018

В итоге я создал собственный список подключенных пользователей, аналогичный ответу, предоставленному Марком К..

Просто чтобы немного расширить его, это позволит мне сделать что-то вроде следующей концепции:

public async Task CheckAllMessages()
{
    foreach (var user in ConnectedUsers)
    {
       var nr = GetNrOfMessagesForUser(user);
       if(nr > 0)
       {
           await Clients.User(user)
                        .SendAsync("ReceiveMessage", 
                                   $"User ({user}) has {nr} new messages waiting.");
       }
    }
}
person Kjartan    schedule 13.08.2018