SignalR:如何从服务器真正调用集线器的方法/C# [英] SignalR: How to truly call a hub's method from the server / C#

查看:20
本文介绍了SignalR:如何从服务器真正调用集线器的方法/C#的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  • 调用API将数据添加到数据库
  • 将新记录返回到AngularJS控制器
  • 从控制器调用集线器的方法
  • 集线器适当地向客户端广播呼叫

我想做的是绕过从我的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连接ID包括在API调用的自定义请求头中,在本例中为INSERT:

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

然后,在执行了适用的CRUD操作之后,我在API控制器中从请求中检索了连接ID,然后调用了My 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);
}

我的集线器如下所示,我现在只使用从接口控制器调用的静电方法:

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方法中有一个条件语句来处理。这是在外部应用程序/服务调用My API的情况下进行的。在这种情况下,SignalR连接当然也不存在"ConnectionId"头值。不过,在我的示例中,我仍然希望为所有连接的客户端调用onInsertTask方法,以通知它们数据更改。这不应该发生,但我只是出于完整性考虑将其包括在内。

推荐答案

为了真正地调用您所调用的集线器方法,您必须连接到它,并通过该连接进行调用。通过调用不同的东西(您的API),您不能进行这种类型的调用,因此您必须求助于服务器发起的广播功能,该功能本质上无法知道Caller是什么,因为没有SignalR的调用方。

也就是说,如果您的客户端在执行调用时调用API(无论是Javascript还是C#)已经连接到集线器,则始终可以使用集线器连接的connectionId修饰对API的调用(按查询字符串、按标头等)。如果您的API收到该信息,则可以使用Caller

模拟CallerAPI
Clients.Client(connectionId)

它可以对Others使用

执行相同的操作
Clients.AllExcept(connectionId)

IHubContext实例上。检查official docs

然后您可以按照DDan的建议,以一种方便的集中方式封装IHubContext用法,甚至对其稍加调整,使其轻松兼容DI。

这篇关于SignalR:如何从服务器真正调用集线器的方法/C#的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆