首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >SignalR:如何从服务器/ C#真正调用集线器的方法

SignalR:如何从服务器/ C#真正调用集线器的方法
EN

Stack Overflow用户
提问于 2015-07-02 02:59:22
回答 2查看 16.7K关注 0票数 19

我正在努力改进我的应用程序,它需要从C#调用集线器,而不是javascript。在我的应用程序中添加任务的当前工作流程是:

  • 发出API调用以将数据添加到控制器中的controller
  • invoke新记录到
  • 集线器的方法

适当地向客户端广播调用

我想要做的是绕过从我的AngularJS控制器调用集线器的方法,而直接从我的API控制器方法调用它。

这是我的集线器当前的样子:

public class TaskHub : Hub
{
    public void InsertTask(TaskViewModel task)
    {
        Clients.Caller.onInsertTask(task, false);
        Clients.Others.onInsertTask(task, true);
    }
}

有很多关于这个主题的线程,但我读到的所有内容都会让我在API控制器中添加以下代码:

var hubContext = GlobalHost.ConnectionManager.GetHubContext<TaskHub>();
hubContext.Clients.All.onInsertTask(task);

这样做有很多问题。首先,我希望客户端广播调用存在于单个类中,而不是直接从我的API控制器调用。其次,hubContextIHubContext而不是IHubCallerConnectionContext的实例。这意味着我只能访问所有客户端,不能像现在这样向CallerOthers广播不同的响应。

有没有一种方法可以真正从C#调用集线器的方法,并且在理想情况下,可以访问不同的调用者选项?理想情况下,我可以在我的API控制器上做如下简单的事情(或者更好的是,使用DI解决方案):

var taskHub = new TaskHub();
taskHub.InsertTask(task);

提前谢谢。

解决方案

对于后人,我认为我应该根据Wasp的建议包含我的完整解决方案。

首先,我修改了我的javascript (AngularJS)服务,以便在SignalR调用的自定义请求头中包含API连接ID,在本例中是一个插入:

var addTask = function (task) {
    var config = { 
        headers: { 'ConnectionId': connection.id } 
    };
    return $http.post('/api/tasks', task, config);
};

然后,在执行了适用的CRUD操作之后,我从API控制器中的请求中检索到了连接ID,然后调用了我的hub:

public HttpResponseMessage Post(HttpRequestMessage request, [FromBody]TaskViewModel task)
{
    var viewModel = taskAdapter.AddTask(task);
    var connectionId = request.Headers.GetValues("ConnectionId").FirstOrDefault();
    TaskHub.InsertTask(viewModel, connectionId);
    return request.CreateResponse(HttpStatusCode.OK, viewModel);
}

我的hub看起来像这样,我现在只使用从API控制器调用的静态方法:

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

    public static void InsertTask(TaskViewModel task, string connectionId)
    {
        if (!String.IsNullOrEmpty(connectionId))
        {
            context.Clients.Client(connectionId).onInsertTask(task, false);
            context.Clients.AllExcept(connectionId).onInsertTask(task, true);
        }
        else
        {
            context.Clients.All.onInsertTask(task, true);
        }
    }
}

正如您所看到的,如果hub调用不是从我的应用程序的客户端部分发起的,我在hub方法中有一个条件语句来处理。这是如果外部应用程序/服务调用我的API的话。在这种情况下,不存在SignalR连接,当然也不存在"ConnectionId“标头值。在我的例子中,我仍然希望为所有连接的客户端调用onInsertTask方法,该方法将数据更改通知给它们。这种情况永远不会发生,但我只是为了完整性而将其包含在内。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-07-02 16:54:46

为了真正调用hub方法,您必须连接到该方法,并通过该连接进行调用。通过调用不同的东西(你的应用程序接口),你不能做这种调用,因此你必须求助于服务器发起的广播功能,它本质上不知道Caller是什么,因为没有SignalR的调用者。

也就是说,如果在执行调用时,您的客户端调用API (无论是Javascript还是C#)已经连接到hub,那么您总是可以使用hub连接的connectionId (通过查询字符串,通过headers,...)来修饰对API的调用。如果您的API接收到该信息,则可以使用以下命令模拟Caller API:

Clients.Client(connectionId)

它可以对Others做同样的事情,比如

Clients.AllExcept(connectionId)

IHubContext实例上。检查official docs

然后,您可以按照DDan的建议,以一种方便的集中式方式封装IHubContext使用,或者甚至稍微调整它以使其更容易符合DI。

票数 7
EN

Stack Overflow用户

发布于 2015-07-02 10:42:52

我正在使用this answer中解释的方法。

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 groupName, string content)
    {
        Clients.Group(groupName).addMessage(content);
    }

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

}

集线器定义并使用其hubContext,因此您可以执行以下操作:

var newsFeedHub = new NewsFeedHub();
var newsFeedHub.Static_Send("ch1", "HELLO");

或者:

var taskHub = new TaskHub();
var taskHub.InsertTask(task);

如果你愿意,基于你的方法命名。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31169509

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档