3
votes

I am trying to send a message from a controller to client(s) group using SignalR. When an event occurs in one of my controllers, I need to push a message using a signalr hub to be displayed like an alert on my clients screens. i know a lot of questions have been asked here, and I've read and tried a lot of them. Since I am new to SignalR some of them even already helped me to put things in place. At the moment everything seems in place. Clients can connect to the hub and join groups and controller can call methods from the hub. But the client never receive the message and I can't figure out why. I suspect that hub's method called by controller don't "see" the clients but I can't understand what's wrong.

Hub's code :

public static class UserHandler
{
    public static HashSet<string> ConnectedIds = new HashSet<string>();
}

[HubName("myHub")]
public class MyHub : Hub
{
    private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
    public void Notify(string groupName, string message)
    {
        Clients.Group(groupName).displayNotification(message);
    }

    public static void Static_Notify(string groupName, string message)
    {
        var toto = UserHandler.ConnectedIds.Count();
        hubContext.Clients.Group(groupName).displayNotification(message);
        hubContext.Clients.All.displayNotification(message);//for testing purpose
    }

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

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

    public override Task OnConnected()
    {
        UserHandler.ConnectedIds.Add(Context.ConnectionId);
        return base.OnConnected();
    }

    public override Task OnDisconnected(bool StopCalled)
    {
        UserHandler.ConnectedIds.Remove(Context.ConnectionId);
        return base.OnDisconnected(StopCalled);
    }
}

Here is the call from my controller :

//(For simplification and readability I define here variables actually obtained by treating some data )
//I already checked that problem did not come from missing data here
string groupName = "theGroupName";
string message = "My beautifull message.";

//prepare signalR call
var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();

//Following commented lines are different attempts made based on exemples and answers I found here and on others sites.
//The uncommented one is the one in use at the moment and is also based on an answer (from SO i think)

//context.Clients.Group(_userEmail).displayNotification(message);
//context.Clients.Group(_userEmail).Notify(_userEmail,message);
  MyHub.Static_Notify(_userEmail, message);

And here is the client-side code :

$(document).ready(function () {
    var userGroup = 'theGroupName';
    $.connection.hub.url = 'http://localhost/SignalRHost/signalr';
    var theHub = $.connection.myHub;
    console.log($.connection)
    console.log($.connection.myHub)

    theHub.client.displayNotification = function (message) {
        console.log('display message');
        alert(message);
    };

    $.connection.hub.start()
        .done(function () {
            theHub.server.joinGroup(userGroup);
            console.log("myHub hub started : " + $.connection.hub.id)
            console.log(theHub)
        })
        .fail(function () {
            console.log('myHub hub failed to connect')
        });

});

Please help me understand what logic I failed to understand or what error I am missing.

EDIT :

To answer Alisson's comment: The Startup.cs I forgot to show

public void Configuration(IAppBuilder app)
    {
        app.Map("/signalr", map =>
        {
            map.UseCors(CorsOptions.AllowAll);
            var hubConfiguration = new HubConfiguration { };
            map.RunSignalR();
        });
    }

Important stuff I forgot to mention too :

  • The Controller, the hub, and the client are 3 different projects (because of the global application architecture I had to separate hub logic) All are on my localhost IIS but on different ports
  • I've set breakpoints on "OnConnected" and "onDiconnected" events and client connects and disconnects normally
1
Is your MVC project being ran in your localhost? I mean, did you publish your project in your local IIS, or are you debugging it with IIS Express from Visual Studio? Can you share how you configured SignalR in your Startup.cs file?Alisson
Thanks @Alisson for pointing important infos I forgot to mention. I answered your questions at the bottom of the post.Grey
If the controllers and hubs are in different projects, that's why it doesn't work. You have two projects acting as a SignalR server: the controller's project, and the hub's project (even though you share the same Hub class in both projects). When you call MyHub.Static_Notify(_userEmail, message); in your controller, your Hub is calling clients connected to the controllers project, but your clients are actually connected to the SignalRHost project. You should either use one unique project for Hubs and Controllers, or make sure your controller invoke the Hub in the SignalRHost project.Alisson
Cna you add a console.log(theHub.client) and add the result to your question?Hackerman
Thanks a lot @Alisson, I'll work on a way to call the hub in the right controller. If I could accept a comment as an answer I'd accept yours.Grey

1 Answers

1
votes

You just need one app to act as server, in your case it should be the SIgnalRHost project. Your controller project should be a client of the server, therefore it just needs this package:

Install-Package Microsoft.AspNet.SignalR.Client 

Your controller project doesn't actually need to reference the project containing the hub class. In your controller, you'll use C# SignalR client to connect to the server (as you would do in javascript client), join the group and invoke the hub method:

var hubConnection = new HubConnection("http://localhost/SignalRHost/signalr");
IHubProxy myHub = hubConnection.CreateHubProxy("MyHub");
await hubConnection.Start();
myHub.Invoke("JoinGroup", "theGroupName");
myHub.Invoke("Notify", "theGroupName", "My beautifull message.");

...finally, you don't need that Static_Notify at all.

Since you are passing the group name as a parameter on the Notify method, you don't really need to join the group from your controller. You'd need only if you were trying to send the message to the same group the controller was connected (then you wouldn't need to pass the group name as parameter anymore).

SignalR C# Client Reference.