Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SignalR push-notification to specific client with MVC4

I am trying to use SignalR with MVC4 c# project to send push notification to selected clients only. For this I am using 'context.Clients.Client' by storing 'Context.ConnectionId' in c# static variable {since unable to use session with signalr}. But since 'contextConnectionId' is a static variable; the message is still boardcasted to all clients :(

Is there any alternative to session with signal or any other elegant way to do this? (found few related post but no specific answers)

Code as below:

javaScript

 $(document).ready(function (){
 {
    var chat = $.connection.PushNotify;
    chat.client.addNewMessageToPage = function (type, message) {
        if(type == "something")
        {
            alert(message);
        }
    }
    // Start the connection.
    $.connection.hub.start().done(function () {
        chat.server.send($('').val(), $('').val());
    });
 )}

In c# HUB

[HubName("PushNotify")]
public class PushNotify : Hub
{
    public void Send(string name, string message)
    {
        //assume its c# static variable
        GlobalVariable.contextConnectionId = Context.ConnectionId;
    }
}
public class PushNotification
{
    public void SendMessage(string message, string type = "msg")
    {
        if (!string.IsNullOrEmpty(GlobalVariable.contextConnectionId))
        {
            var context = GlobalHost.ConnectionManager.GetHubContext<PushNotify>();
            context.Clients.Client(GlobalVariable.contextConnectionId).addNewMessageToPage(type, message);
        }

    }
}
like image 797
10K35H 5H4KY4 Avatar asked Jul 03 '15 00:07

10K35H 5H4KY4


1 Answers

I don't think you example will work as expected. When you map your connection to your static variable you actually overwrite your static variable to the last connection which called Send method.

Each client connecting to a hub passes a unique connection id. You can retrieve this value in the Context.ConnectionId property of the hub context.

Which means your GlobalVariable.contextConnectionId cannot be a simple string. You have to map your clients in a way, and every client is going to have a unique connection id. Perhaps a dictionary would be enough here, or own class containing more info. (i.e. browser info, IP adderss...)

(Quote is from this link) This should help you: http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections

EDIT

Providing a bit more information:

I believe the simplest solution for your question is to use groups. http://www.asp.net/signalr/overview/guide-to-the-api/working-with-groups

Your hub class would contain a method to join a group:

public Task JoinGroup(string groupName)
{
    return Groups.Add(Context.ConnectionId, groupName);
}

public Task LeaveGroup(string groupName)
{
    return Groups.Remove(Context.ConnectionId, groupName);
}

And let's say a PostMessage to group which sends to a given group only:

public void SendMessage(string groupName, string message, string type = "msg")
{
    var context = GlobalHost.ConnectionManager.GetHubContext<PushNotify>();
    context.Clients.Group(groupName).addNewMessageToPage(type, message);
}

When your clients join, they can connect to a group, and everything else will be handled by SignalR.

Joining the group from client and posting message:

var chat = $.connection.PushNotify;
$.connection.hub.start().done(function () {
    chat.server.joinGroup("Group1");
    chat.server.sendMessage("Group1", "Hi There");
});

Receiving messages is the same

chat.client.addNewMessageToPage = function (type, message) {
   ...
}

OTHER SOLUTION

Another solution could be using your own mapping or the SignalR Connection mapping. These solutions needs to override the OnConnected and OnDisconnected methods and map your connections. i.e.:

private readonly static ConnectionMapping<string> _connections = 
        new ConnectionMapping<string>();

public override Task OnConnected()
{
    string name = Context.User.Identity.Name;
    _connections.Add(name, Context.ConnectionId);
    return base.OnConnected();
}

public override Task OnDisconnected(bool stopCalled)
{
    string name = Context.User.Identity.Name;
    _connections.Remove(name, Context.ConnectionId);
    return base.OnDisconnected(stopCalled);
}

public void SendChatMessage(string who, string message)
{
    string name = Context.User.Identity.Name;
    foreach (var connectionId in _connections.GetConnections(who))
    {
        Clients.Client(connectionId).addChatMessage(name + ": " + message);
    }
}

Like in this example: http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections#inmemory

like image 84
DDan Avatar answered Sep 16 '22 15:09

DDan