Signalr cast IHubContext to actual Hub - c#

I'm wondering if it's possible to Cast the result of
var hub = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
To my actual ChatHub class.
Because GlobalHost.ConnectionManager.GetHubContext<ChatHub>() as ChatHub fails
On my ChatHub class I have a method UpdateTime():
public void SendTimeUpdate(DateTime time, string auth)
{
Clients.All.UpdateTime(time, auth);
}
And I want to call it from my other class. Since I can't cast to ChatHub and invoke the SendUpdate I have to go:
GlobalHost.ConnectionManager.GetHubContext<ChatHub>().Clients.All.UpdateTime(time, auth);
But if I go this road, the method SendTimeUpdate isn't added in the proxy script /signalr/hubs
Is there a solution for this problem? I want to get the typed Hub instance and not call stuff directly on the Clients property of the IHubContext.

No you cannot cast the result of ....GetHubContext<.... to your hub class. Sorry :(.
The GetHubContext approach returns an IHubContext when a Hub is only an IHub.
If you'd like to centralize the logic just make a method that you can call into from your hub and from your external service.

Couldn't your class just create a connection to your hub and call the method that way?

Related

Instantiate SignalR Hub Object With IHubContext

It seems like a big use for SignalR Hubs is to display the actions of one client to all of the other clients. What I hope to use SignalR for is when a certain event happens in my server side code, I want to instantiate a hub object and invoke one of its methods to communicate with all of the clients. If you see my previous post (Route To Take With SqlDependency OnChange), I would like to do this in the OnChange method of SqlDependency. Upon researching it I have found some people talk about using an IHubContext object, though I haven't found many examples of instantiation and actual sending data to clients.
Is this possible to do (and what might sending data to all clients with IHubContext look like if possible), and if not, are there any ways I might be able to get around instantiating a hub like this?
SignalR for ASP.NET Core
You can create a class that has the IHubContext<T> injected in. Inject other dependencies if you want, or resolve the service from controllers or other classes.
public class NotificationService
{
private readonly IHubContext<MyHub> _myHubContext;
public NotificationService(IHubContext<MyHub> myHubContext)
{
_myHubContext= myHubContext;
}
public async Task SendMessage(string message)
{
await _myHubContext.Clients.All.SendAsync("Update", message);
}
}
Assuming you're using SqlDependency from an IHostedService:
public class MyHostedService : IHostedService
{
public MyHostedService(
NotificationService notificationService)
{
// TODO get reference to sqlDependency
sqlDependency.OnChange += (s, e) => _notificationService.SendMessage(e.Info.ToString());
}
}
SignalR for ASP.NET
var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
context.Clients.All.sendMessage(message);
You need to use using Microsoft.AspNet.SignalR library.
using Microsoft.AspNet.SignalR;
//Instantiating. SignalRHub is the hub name.
var context = GlobalHost.ConnectionManager.GetHubContext<SignalRHub>();
//sends message
context.Clients.Client(ClientId).sendMessage(data);

signalr how i can i post a message from server to caller

I am using Signalr 1.1.4 because im still using .net4 so cant upgrade to signalr 2.
Basically i want to post a message from the server to just the caller to avoid messages being sent to any client that did not start the process off.
My hub class looks like this
public class UpdateHub : Hub
{
/// <summary>
/// Sends the message.
/// </summary>
/// <param name="progressMessage">The progress message.</param>
public void SendMessage(string progressMessage)
{
Clients.Client(Context.ConnectionId).sendMessage(string.Format(progressMessage));
}
}
my javascript looks like this
// get handle to subscriptionUpload hub generated by SignalR
var updateHub = $.connection.UpdateHub;
// establish the connection to the server and start server-side operation
$.connection.hub.start();
updateHub.client.sendMessage = function (message)
{
$("container").empty();
$("container").append(message);
}
Now in my controller action method i would like to do something like this
UpdateHub hub = new UpdateHub();
hub.SendMessage("process has started");
//continue on with long process
hub.SendMessage("process has ended");
Is this even possible?
What we can find in documentation documentation:
You don't instantiate the Hub class or call its methods from your own
code on the server; all that is done for you by the SignalR Hubs
pipeline. SignalR creates a new instance of your Hub class each time
it needs to handle a Hub operation such as when a client connects,
disconnects, or makes a method call to the server.
Because instances of the Hub class are transient, you can't use them
to maintain state from one method call to the next. Each time the
server receives a method call from a client, a new instance of your
Hub class processes the message. To maintain state through multiple
connections and method calls, use some other method such as a
database, or a static variable on the Hub class, or a different class
that does not derive from Hub. If you persist data in memory, using a
method such as a static variable on the Hub class, the data will be
lost when the app domain recycles.
And then:
If you want to send messages to clients from your own code that runs
outside the Hub class, you can't do it by instantiating a Hub class
instance, but you can do it by getting a reference to the SignalR
context object for your Hub class.
You can get the context of your hub: GlobalHost.ConnectionManager.GetHubContext<YourHub>()
and then you can use it to call methods on the client side like this:
context.Clients.All.YourMethod(params);
or
context.Clients.Client(someConnectionID).YourMethod(params);
But in this case you won't be able to use Context.ConnectionId in this methods, because you don't have a direct connection to your hub. In this case you will need to store your connections somewhere (static variables, cache, db etc) and then use it to determine which client should be called.
Hope it will help.

Does my SignalR Hub class need any methods?

According to the documentation I have read, in order to send a message to a client, I just need to call:
var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
hubContext.Clients.All.foo(msg);
So... does the Hub class need any methods? If not, then all I have is an empty class:
public class MyHub : Hub
{
}
which just seems like a pointless setup. Am I implementing this incorrectly? because it makes more sense to have methods in a hub class, and then call those methods to send a message to the client.
Also, in the hub itself, I can access Context.connectionId, so that I can get the requestor's connection Id and then stop the message from being fired to that client.... If a Hub shouldn't have methods, then is there a way to access the requestor's connection id?
Yes, you need an empty HUB class declaration, because - It is actually just a proxy between the JS client and the controller so it could be empty since all methods are called via the Clients dynamic variable.
I mean without this , you can work, but you have to write JS for that. This is explained briefly in following link.
For more info refer this link - http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-javascript-client#genproxy

How is this code being reached in SignalR

I have the following method in my Hub class:
public class NotificationHub : Hub
{
public void SendAll(string message)
{
if (1 == 0)
Clients.All.broadcastMessage(message); // this should be unreachable
}
}
Then, I (am trying) to call that method from my server-side code like so:
GlobalHost.ConnectionManager.GetHubContext<NotificationHub>()
.Clients.All.broadcastMessage("broadcastMessage was called");
The method is called, and everything works. But, I didn't want broadcastMessage() to be called since it should have been unreachable.
I read this from the documentation:
You don't instantiate the Hub class or call its methods from your own
code on the server; all that is done for you by the SignalR Hubs
pipeline. SignalR creates a new instance of your Hub class each time
it needs to handle a Hub operation such as when a client connects,
disconnects, or makes a method call to the server.
Ref. http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server
But it doesn't look like it uses my methods at all. It just looks like it calls its own methods and ignores mine. How can I call my own methods using SignalR?
When you do this:
GlobalHost.ConnectionManager.GetHubContext<NotificationHub>()
.Clients.All.broadcastMessage("broadcastMessage was called");
You're bypassing the method
public void SendAll(string message)
I am not sure why you'd expect the first method to be blocked. If you want your hub logic to work you have to work through the hub methods (example: public void SendAll(string message))
I like the solution presented here: https://stackoverflow.com/a/17897625/693272 if you want to call the hub method from outside.

SignalR - Updating clients from Global.asax

Suppose I have a ChatHub class defined along with a Broadcast method.
I know how to broadcast messages to all clients if one of them sends a message, however how can I send a message to all the clients from Global.asax ?
In other words how do I get access to ChatHub from another class?
Here's a basic sample:
public class ChatHub : Hub
{
public void Broadcast(String reqMessage)
{
Clients.broadcast(reqMessage);
}
}
Your help is much appreciated.
Since SignalR 0.5 you can do this using GlobalHost.ConnectionManager.GetHubContext
Sample
// get gub context
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
// broadcast to all clients in this hub
context.Clients.broadcast("Hello World");
More Information
SignalR - Hubs
Use ConnectionManager, as described here: https://github.com/SignalR/SignalR/wiki/Hubs.
Btw, your question is most likely a duplicate of this one.

Categories