I have a self-hosted SignalR application running in the context of a console app. I'm connecting to the hubs within it through the use of a wrapper class to prevent me from having to reference the SignalR.Core assemblies from my ASP.NET project. So it's another C# class from within my application that's responsible for broadcasting messages to connected clients.
I'm able to call the PlanHub
methods (Register
and Unregister
) directly from Javascript, those breakpoints get hit. However, when I call client methods from a class outside of the Hub (even if I use Clients.All
to eliminate problems with the Group registration), the clients never get the message. What am I doing wrong?
When running this code, I can verify that the Clients.All.updateStatus(planId, message);
code is being hit successfully, but nothing is logged to the console on the client.
Here's the relevant code:
PlanHub.cs
public class PlanHub : Hub
{
private const string GroupPrefix = "PlanGroup_";
public void Register(int companyId)
{
Groups.Add(Context.ConnectionId, $"{GroupPrefix}{companyId}");
}
public void Unregister(int companyId)
{
Groups.Remove(Context.ConnectionId, $"{GroupPrefix}{companyId}");
}
}
PlanPublisher.cs
public class PlanPublisher
{
private readonly static Lazy<PlanPublisher> _instance = new Lazy<PlanPublisher>(() =>
new PlanPublisher(GlobalHost.ConnectionManager.GetHubContext<PlanHub>().Clients));
private IHubConnectionContext<dynamic> Clients { get; set; }
private PlanPublisher(IHubConnectionContext<dynamic> clients)
{
Clients = clients;
}
public static PlanPublisher Instance => _instance.Value;
public void UpdateStatus(int planId, string message)
{
//This code gets hit and no exceptions are thrown, but the clients
//never receive the message. Using Clients.All instead of my
//Groups for testing -- still doesn't work
Clients.All.updateStatus(planId, message);
}
}
Calling code (from within another C# class)
PlanPublisher.Instance.UpdateStatus(plan.Id, $"Publishing started for {plan.Name}...");
Javascript
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<script src="~/scripts/jquery.signalR-2.2.0.min.js"></script>
<script src="http://localhost:8080/signalr/hubs"></script>
<script>
$(document).ready(function() {
$.connection.hub.url = "http://localhost:8080/signalr";
var planMessagePublisher = $.connection.planHub;
planMessagePublisher.client.updateStatus = function (planId, message) {
console.log('Status: ' + planId + ' - ' + message);
};
$.connection.hub.start().done(function() {
//This works! My PlanHub.Register method gets called successfully
planMessagePublisher.server.register(@Model.CompanyId);
// New connection ID is successfully sent to console
console.log('Now connected, connection ID=' + $.connection.hub.id);
});
});
</script>
JS Console Debugger Output
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Auto detected cross domain url.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Client subscribed to hub 'planhub'.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Negotiating with 'http://localhost:8080/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%7B%22name%22%3A%22planhub%22%7D%5D'.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: webSockets transport starting.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Connecting to websocket endpoint 'ws://localhost:8080/signalr/connect?transport=webSockets&clientProtocol=1.5&connectionToken=AQAAANCMnd8BFdERjHoAwE%2FCl%2BsBAAAA5D8YUVyzBEG4tTlTaGn0MgAAAAACAAAAAAADZgAAwAAAABAAAABDjF1MaCTzZI0XTM8gC29xAAAAAASAAACgAAAAEAAAAKrk0jv%2FKF4YFzDvNwmSR8IoAAAAacm1d1r7dJpjOtVtCFIFRpugkubyZm1e5Z8OtOFtnhZyEBO1SO4lqhQAAABiG7hBydTiypPh8k2ZYz20ropNxw%3D%3D&connectionData=%5B%7B%22name%22%3A%22planhub%22%7D%5D&tid=6'.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Websocket opened.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: webSockets transport connected. Initiating start request.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: The start request succeeded. Transitioning to the connected state.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Now monitoring keep alive with a warning timeout of 13333.333333333332, keep alive timeout of 20000 and disconnecting timeout of 30000
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Invoking planhub.Register
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Invoked planhub.Register
It registers successfully, but never receives any data. The Clients.All.updateStatus(planId, message);
breakpoint was hit multiple times but the console never received any additional logging data.
EDIT: It was suggested that I look into whether or not a custom dependency resolver is in play. The code posted here lives in its own project with very little else, other than what you see here. The code that calls PlanPublisher.UpdateStatus()
DOES have a custom dependency resolver, but that shouldn't matter since it's isolated in its own assembly. PlanPublisher.cs
and PlanHub.cs
are contained in a very simple project that just has references to SignalR.Core and SignalR.SelfHost.
I tried to simplify your example (using SignalR 2.2.0)
Hub class:
public class PlanHub : Hub
{
private const string GroupPrefix = "PlanGroup_";
// hubcontext
private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext <PlanHub> ();
public void Register(int companyId)
{
Groups.Add(Context.ConnectionId, $"{GroupPrefix}{companyId}");
}
public void Unregister(int companyId)
{
Groups.Remove(Context.ConnectionId, $"{GroupPrefix}{companyId}");
}
// static method using hub context
public static void Static_UpdateStatus(int planId, string message)
{
hubContext.Clients.All.updateStatus(planId, message);
}
}
Since I don't really have a backend and I don't know how you are going to do that, I just set up a timer to call the method from C# every 10 seconds.
Calling and dummy timer:
public void startTimer()
{
timer = new System.Timers.Timer();
timer.Elapsed += new System.Timers.ElapsedEventHandler(ExecuteTimerJob);
timer.Interval = 10000; // 10 sec
timer.Start();
}
public static void ExecuteTimerJob(object source, System.Timers.ElapsedEventArgs e)
{
// this is the call from C#
PlanHub.Static_UpdateStatus(123, "some message");
}
JS:
$(function () {
var planHub = $.connection.planHub;
$.connection.hub.start().done(function () {
console.debug('Connected to PlanHub...');
})
planHub.client.updateStatus = function (planId, message) {
console.log("planId: " + planId);
console.log("message: " + message);
}
});
Result in browser console:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With