52

How can I call a hub method from a controller's action? What is the correct way of doing this?

Someone used this in a post:

DefaultHubManager hd = new DefaultHubManager(GlobalHost.DependencyResolver);
var hub = hd.ResolveHub("AdminHub") as AdminHub;
hub.SendMessage("woohoo");

But for me, that is throwing:

Using a Hub instance not created by the HubPipeline is unsupported.

I've read also that you can create a hub context, but I don't want to give the responsability to the action, that is, the action doing stuff like:

hubContext.Client(...).someJsMethod(..)
sports
  • 7,851
  • 14
  • 72
  • 129
  • possible duplicate of [SignalR + posting a message to a Hub via an action method](http://stackoverflow.com/questions/7549179/signalr-posting-a-message-to-a-hub-via-an-action-method) – Tim B James Jul 10 '13 at 09:26

2 Answers2

113

The correct way is to actually create the hub context. How and where you do that is up to you, here are two approachs:

  1. Create a static method in your hub (doesn't have to be in your hub, could actually be anywhere) and then you can just call it via AdminHub.SendMessage("wooo")

    public static void SendMessage(string msg)
    {
        var hubContext = GlobalHost.ConnectionManager.GetHubContext<AdminHub>();
        hubContext.Clients.All.foo(msg);
    }
    
  2. Avoid the static method all together and just send directly to the hubs clients

        var hubContext = GlobalHost.ConnectionManager.GetHubContext<AdminHub>();
        hubContext.Clients.All.foo(msg);
    
N. Taylor Mullen
  • 18,061
  • 6
  • 49
  • 72
  • 1
    how about i will call the caller in this case?? we cannot call the Clients.Caller in this part right?? how would be the approach for that?? – ECie Feb 12 '15 at 03:40
  • 2
    No you cannot do .Caller directly. You will need to track your users ConnectionIds and then you can do `hubContext.Client(theusersconnectionid).foo(msg) – N. Taylor Mullen Feb 12 '15 at 19:07
  • 4
    I can't see that the (server) hub method is called from the controller action using your methods and wasn't that the question by "sports"?. – AH. Feb 07 '17 at 07:53
  • Might want to look into creating a singleton as exampled here: "https://learn.microsoft.com/en-us/aspnet/signalr/overview/older-versions/tutorial-server-broadcast-with-aspnet-signalr" to access the context once, as according to the article getting the context is costly. – eaglei22 May 02 '17 at 17:11
  • This has worked in my small sample/test: on the client, I just get the connection Id and send that up as an argument to my controller action. js: $.connection.hub.id . However, I'm still learning this stuff, so I may not be correct, but it SEEMS to be working for now. Anyway, then I get the hubcontext (as above) and say hubContext.Clients.Client(clientId).foo("msg") – emery.noel Mar 05 '18 at 22:00
6

As per aspnet3.1

This differs from ASP.NET 4.x SignalR which used GlobalHost to provide access to the IHubContext. ASP.NET Core has a dependency injection framework that removes the need for this global singleton.

The currently suggested way to do this is by Dependency Injection. You can read more about that here.

https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-3.1


Snippet from above

public class HomeController : Controller
{
    private readonly IHubContext<NotificationHub> _hubContext;

    public HomeController(IHubContext<NotificationHub> hubContext)
    {
        _hubContext = hubContext;
    }
}

Then call it like so

public async Task<IActionResult> Index()
{
    await _hubContext.Clients.All.SendAsync("Notify", $"Home page loaded at: {DateTime.Now}");
    return View();
}
Jack
  • 2,891
  • 11
  • 48
  • 65