66

I am using the hub- feature of SignalR (https://github.com/SignalR/SignalR) to publish messages to all subscribed clients:

public class NewsFeedHub : Hub
public void Send(string channel, string content)
{
    Clients[channel].addMessage(content);
}

This works fine when calling "Send" via Javascript, but I would also like the web application to publish messages (from within an ASP.NET MVC action method). I already tried instantiating a new object ob NewsFeedHub and calling the Send method, but this results in an error (as the underlying "Connection" of the Hub is not set). Is there a way to use the Hub without a connection?

Ashley Medway
  • 7,151
  • 7
  • 49
  • 71
ollifant
  • 8,576
  • 10
  • 35
  • 45
  • 2
    did you find the answer this? i'm in the same predicament. Signalr dll seems to initialise the hub object, so i was wondering if i needed to get access to that. – Mike Sep 26 '11 at 08:42

5 Answers5

96

Please note that the SignalR API has changed multiple times since this question was asked. There is a chance that some answers will become out of date.

There is another updated answer for this, as seen in the SignalR Wiki

c#

public ActionResult MyControllerMethod()
{
    var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
    context.Clients.All.methodInJavascript("hello world");
    // or
    context.Clients.Group("groupname").methodInJavascript("hello world");
}

vb.net

Public Function MyControllerMethod() As ActionResult
    Dim context = GlobalHost.ConnectionManager.GetHubContext(Of MyHub)()
    context.Clients.All.methodInJavascript("hello world")
    '' or
    context.Clients.Group("groupname").methodInJavascript("hello world")
End Function

Update

This code has been updated. Follow http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server for changes.

If you are using DI container

If you are using a DI container to wire up your hubs, get IConnectionManager from your container, and call GetHubContext on that connectionManager.

E_net4
  • 27,810
  • 13
  • 101
  • 139
Tim B James
  • 20,084
  • 4
  • 73
  • 103
  • Works great! :) Solved my problem. I was having serious problems with a piece of code I got here: http://www.xhroot.com/blog/2012/07/12/live-updates-using-signalr/ – Leniel Maccaferri Jul 29 '12 at 07:43
  • 2
    *This does not mean that they should be down-voted as they were correct at the time of writing*? Surely they should be downvoted if they are not correct anymore? – Liam Apr 21 '17 at 13:29
  • 4
    Ideally the Tags should be version specific for historical accuracy. – Tim B James Apr 21 '17 at 14:07
63

2018 February, Short and simple solution

For solving this I usually design my hubs in the following way:

public class NewsFeedHub : Hub 
{
    private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<NewsFeedHub>();

    // Call this from JS: hub.client.send(channel, content)
    public void Send(string channel, string content)
    {
        Clients.Group(channel).addMessage(content);
    }

    // Call this from C#: NewsFeedHub.Static_Send(channel, content)
    public static void Static_Send(string channel, string content)
    {
        hubContext.Clients.Group(channel).addMessage(content);
    }

}

So it's easy to call from Javascript and from back-end code as well. The two methods are resulting in the same event at the client.

DDan
  • 8,068
  • 5
  • 33
  • 52
  • 1
    August 2016, only change I had to do is: Clients[channel] -> Clients.Group(channel) – Jorge Rodrigues dos Santos Aug 22 '16 at 18:21
  • 2
    This is hands down the best and most efficient solution I've come across, it is reusable code, and works perfectly. – Jorge Cuevas Mar 22 '17 at 18:42
  • The static method does not work for me. I debug and step through it, it tries to send the JavaScript, but nothing happens. The regular, non-static method works, however, doing the exact same thing. Problem is that I can't call it from Web API, the thing I'm trying to do. Ideas? EDIT: this is probably horrible, but in my non-static method, I save off "Clients" to a private static variable (of IHubConnectionContext) and then call it in my static method. Works, but... doesn't feel right. – vbullinger Apr 25 '17 at 21:20
  • This worked for me as well. Furthermore, is there any particular reason for the static version of the method? I had a need for access to the hub from a service layer, so I obtained an instance of my IProgressUpdateHub via IoC. I can't implement static methods on an interface, so I just changed the non-static method to call into the context's Clients collection. – Jonathan Shay Aug 24 '17 at 13:38
  • The issue with this is then you no longer have access the instance variables, say something that keeps track of connectionIds and user ids. – Mukus Mar 29 '19 at 06:31
  • Works for me in asp.net 4.8 but putting "IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext(); " inside method "public static void Static_Send(string channel, string content)" signalr version 2.2.3 and jquery 3.3.1 – Javier Cañon Dec 07 '21 at 01:29
  • After 7 years this approach still rocks. In my earlier company I made this to be part of the standard scaffolding or our interactive hubs with code generation tricks, and to date, it is core part of the product there, making hub methods easily accessible from JS or C# code in a seamless way. Glad to see this working. – DDan Aug 22 '22 at 05:16
10

update 2012: This answer is old, too! SignalR's public API is in constant flux, it seems. Tim B James has the new, proper API usage as of July 2012.

update 2019 Don't use this answer anymore. New versions of SignalR that work on AspNetCore should refer to the accepted answer by Tim B James, or other up-voted answers. I'm leaving this answer here for history's sake.


The currently accepted answer from Mike is old, and no longer works with the latest version of SignalR.

Here's an updated version that shows how to post a message to a hub from an MVC controller action:

public ActionResult MyControllerMethod() 
{
     // Important: .Resolve is an extension method inside SignalR.Infrastructure namespace.
     var connectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
     var clients = connectionManager.GetClients<MyHub>();

     // Broadcast to all clients.
     clients.MethodOnTheJavascript("Good news!");

     // Broadcast only to clients in a group.
     clients["someGroupName"].MethodOnTheJavascript("Hello, some group!");

     // Broadcast only to a particular client.
     clients["someConnectionId"].MethodOnTheJavascript("Hello, particular client!");
 } 
Judah Gabriel Himango
  • 58,906
  • 38
  • 158
  • 212
7

I was looking for .NET Core solution and Google sent me here, seems like no one mentioned .NET Core solution, so here you go:

The Hub, nothing fancy here:

public class MyHub : Hub
{
    // ...
}

Inside your controller:

public class HomeController : Controller
{
    readonly IHubContext<MyHub> _hub;

    public HomeController(IHubContext<MyHub> hub)
    {
        _hub = hub;
    }

    public async Task<IActionResult> Index()
    {
        // ...
        await _hub.Clients.All.SendAsync("ReceiveMessage", "Hello from HomeController");
        // ...
    }
}
Mehdi Dehghani
  • 10,970
  • 6
  • 59
  • 64
  • 1
    As of June 2021, this is the only working code. Let's hope the API will stay for a while. Thanks Mehdi – WSK Jun 18 '21 at 20:44
  • While this might work in some cases, it doesn't really answer the question since it doesn't call the `Send` method in the hub, but rather interacts directly with clients. – Magnus Sep 06 '22 at 09:07
  • @Magnus OP wants to send a message via an action and that is exactly what this code is doing. – Mehdi Dehghani Sep 06 '22 at 09:54
3

Following on from DDan's answer you can create an overloaded static method and keep common logic in one method - I use this pattern

public class ChatHub : Hub
{
    private static IHubConnectionContext<dynamic> GetClients(ChatHub chatHub)
    {
        if (chatHub == null)
            return GlobalHost.ConnectionManager.GetHubContext<ChatHub>().Clients;
        else
            return chatHub.Clients;
    }

    // Call from JavaScript
    public void Send(string message)
    {
        Send(message, this);
    }

    // Call from C# eg: Hubs.ChatHub.Send(message, null);
    public static void Send(string message, ChatHub chatHub)
    {
        IHubConnectionContext<dynamic> clients = GetClients(chatHub);
        // common logic goes here 
        clients.All.receiveSend(message);
    }
}

Then from your controller you can use

Hubs.ChatHub.Send(arg1, null);
J45
  • 374
  • 3
  • 14