SignalR + 通过动作方法向Hub发布消息

66

我正在使用SignalR的hub特性(https://github.com/SignalR/SignalR),将消息发布给所有订阅的客户端:

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

当通过Javascript调用“Send”时,这很好用,但我还希望Web应用程序能够从ASP.NET MVC操作方法中发布消息。我已经尝试了实例化一个新的NewsFeedHub对象并调用Send方法,但这会导致错误(因为Hub的底层“Connection”未设置)。有没有一种方法可以在没有连接的情况下使用Hub?


2
你找到答案了吗?我也遇到了同样的困境。Signalr DLL 似乎初始化了 Hub 对象,所以我想知道是否需要访问它。 - Mike
5个回答

96
请注意,自提出此问题以来,SignalR API 已经多次更改。有可能一些答案已经过时。
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

更新

此代码已更新。请访问http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server查看更改内容。

如果您正在使用 DI 容器

如果您正在使用 DI 容器来连接您的通讯中心,请从容器中获取IConnectionManager,并在该连接管理器上调用 GetHubContext


非常好用! :) 解决了我的问题。我在这里得到的一段代码一直让我头疼:http://www.xhroot.com/blog/2012/07/12/live-updates-using-signalr/ - Leniel Maccaferri
2
这并不意味着它们应该被踩,因为它们在撰写时是正确的。如果它们不再正确,那么肯定应该被踩吧? - Liam
4
理想情况下,标签应该是版本特定的,以确保历史准确性。 - Tim B James

63

2018年2月,简短而简单的解决方案

为了解决这个问题,我通常按照以下方式设计我的中心站:

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);
    }

}

因此,从JavaScript和后端代码中调用很容易。这两种方法在客户端产生相同的事件。


1
2016年8月,我唯一需要做的更改是:Clients[channel] -> Clients.Group(channel)。 - Jorge Rodrigues dos Santos
2
这无疑是我遇到的最好、最高效的解决方案,它是可重用的代码,并且完美地运行。 - Jorge Cuevas
静态方法对我不起作用。我进行了调试并逐步执行它,它尝试发送JavaScript,但什么也没有发生。然而,常规的非静态方法可以正常工作,执行完全相同的操作。问题是我无法从Web API中调用它,这是我正在尝试做的事情。有什么想法吗?编辑:这可能很糟糕,但在我的非静态方法中,我将“Clients”保存到一个私有静态变量(IHubConnectionContext <dynamic>),然后在我的静态方法中调用它。虽然可行,但感觉不太对。 - vbullinger
在asp.net 4.8中对我有效,但将"IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<NewsFeedHub>();"放置在方法"public static void Static_Send(string channel, string content)"内,需要使用signalr版本2.2.3和jquery 3.3.1。 - Javier Cañon
经过7年的时间,这种方法仍然非常有效。在我之前的公司中,我将其作为标准脚手架或我们交互式中心的一部分,并使用代码生成技巧,到目前为止,它是产品的核心部分,使得中心方法可以轻松地从JS或C#代码中无缝访问。很高兴看到这个方法仍然有效。 - DDan
显示剩余2条评论

10

更新于2012年这个答案也已经过时了!SignalR的公共API似乎在不断变化。截至2012年7月,Tim B James有新的、正确的API用法。

更新于2019年:不再使用此答案。适用于AspNetCore的新版本SignalR应参考Tim B James或其他点赞的答案。我将保留这个答案以供历史记录。


来自Mike的当前被接受的答案已经过时,不再适用于最新版本的SignalR。

下面是一个更新的版本,展示了如何从MVC控制器操作中向一个hub发布一条消息:

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!");
 } 

作为提示,对于任何尝试此操作的人来说,看起来这些方法已不再可用。感谢Tim提供新方法的更新。看起来这个情况还在不断变化中。 - runxc1 Bret Ferrier
我有点困惑。AspNetHost 不是属于 0.4 版本吗?而不是当前版本(目前为 0.5.2)。这对我来说行不通。 - Matt Roberts
你有看到顶部的更新吗?这个API也已经被弃用了。请阅读Tim B James的回答。 - Judah Gabriel Himango

7

我正在寻找 .NET Core 解决方案,谷歌把我带到了这里,似乎没有人提到 .NET Core 解决方案,所以在这里:

Hub,这里没有花哨的东西:

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

在你的控制器中:

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");
        // ...
    }
}

1
截至2021年6月,这是唯一可用的代码。希望API能够保持稳定一段时间。谢谢Mehdi。 - WSK
虽然在某些情况下这可能有效,但它并没有真正回答问题,因为它没有调用中心的“Send”方法,而是直接与客户端交互。 - Magnus
@Magnus OP想通过一个操作发送一条消息,而这段代码正是在做这件事。 - Mehdi Dehghani

3

在DDan的回答之后,您可以创建一个重载的静态方法并将常见逻辑放在一个方法中 - 我使用这种模式

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);
    }
}

然后在您的控制器中,您可以使用:
Hubs.ChatHub.Send(arg1, null);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接