SignalR - Send message to All, Excluding a user's all connections

asp.net, c#, notifications, signalr

Solution

You should change your `Groups` collection to use the `name` as the key and a List as value.

Then change your `OnConnected()` override to look for the `name` key and add or create the `List<string>` of `connectionId`.

In your "self" hub method, you would want all connections associated with the current user:

List<string> clientWindows; 
Groups.TryGetValue(name, out clientWindows); 
clientWindows.ForEach(connectionId => {
    Clients.Client(connectionId)
         .broadcastNotification(username, page, type, id, title);
});

and in your "others" hub method you would want to take all lists of connections, except the ones associated with that client name:

var others = Groups.Where(n => n.Key != name).SelectMany(s => s.Value);
Clients.AllExcept(others.ToArray())
    .broadcastNotification(username, page, type, id, title);

EDIT: This answer assumed that Groups was a globally defined static dictionary.

Dictionary<string, List<string>> Groups = new Dictionary<string, List<string>>()

To adapt this to the IGroupManager Groups you would need to create a static dictionary to maintain the list of connections associated with a user. Here's the code:

using System.Collections.Generic;
using System.Linq;

private static Dictionary<string, List<string>> userGroups 
                                 = new Dictionary<string, List<string>>()
private static object _lock = new object();

public class NotificationHub : Hub
{
    public void ShowSelfNotification(string page, string type, int id, string title)
    {
        string username = Context.User.Identity.Name;
        Clients.Group(username).broadcastNotification(page, type, id, title);
    }

    public void ShowNotification(string page, string type, int id, string title)
    {
        string username = Context.User.Identity.Name;
        var others = userGroups.Where(n => n.Key != username)
                               .SelectMany(s => s.Value);

        Clients.AllExcept(others.ToArray())
               .broadcastNotification(username, page, type, id, title);
    }

    public override Task OnConnected()
    {
        string name = Context.User.Identity.Name;
        Groups.Add(Context.ConnectionId, name);

        lock(_lock)
        {
            if (userGroups .ContainsKey(name))
                userGroups [name].Add(Context.ConnectionId);
            else
                userGroups .Add(name, new List<string>{Context.ConnectionId})

        }

        return base.OnConnected();
    }

   public override Task OnDisconnected()
   {
       string name = Context.User.Identity.Name;
       lock(_lock)
       {
            if (userGroups .ContainsKey(name))
                userGroups[name].Remove(Context.ConnectionId);
       }
       return base.OnDisconnected();
   }
}

Problem

In my ASP.NET 4.5 WebForms project, I am sending update notifications to all clients that are currently connected. What I have is two Hubs, one of them sending update notifications to a user's other open tabs or windows, and the other is sending update notifications to others. The first hub is like ``` [Authorize] public class SelfNotificationHub : Hub { public void ShowSelfNotification(string page, string type, int id, string title) { string username = Context.User.Identity.Name; Clients.OthersInGroup(username).broadcastNotification(page, type, id, title); } public override Task OnConnected() { string name = Context.User.Identity.Name; Groups.Add(Context.ConnectionId, name); return base.OnConnected(); } } ``` Here I am adding all the connectionIds which are created by a membership user (from every connection user created - tabs, windows...) and sending the notification to all these ids except the caller. The caller's notification is shown by js code. And the other hub is like ``` [Authorize] public class NotificationHub : Hub { public void ShowNotification(string page, string type, int id, string title) { string username = Context.User.Identity.Name; Clients.Others.broadcastNotification(username, page, type, id, title); } } ``` In this one I am sending the notification including the username to notify other users. The problem is, with this configuration user's other open tabs and windows are receiving notifications from both of the Hubs. And in the second hub I cannot disclude a user's connectionIds all together, like `Clients.AllExcept(username)`. AllExcept function requires type "String[] ConnectionIds" and I couldn't figure out how to get the connectionIds in a group. That way, I could create a String[] array from the single user's group and exclude the callerId from that array. That would do the job (I think). I guess I could tell what I need. How could I accomplish that scenario without any headaches? Thanks!

Original source