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

im1*_*ike 19 c# signalr

我正在尝试改进我的应用程序,这将需要从C#而不是javascript调用集线器.在我的应用中添加任务的当前工作流程是:

  • 进行API调用以将数据添加到数据库
  • 将新记录返回给AngularJS控制器
  • 从控制器调用hub的方法
  • 集线器正确地向客户广播

我想做的是绕过从我的AngularJS控制器调用hub的方法,并直接从我的API控制器方法调用它.

这就是我的中心目前的样子:

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

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

var hubContext = GlobalHost.ConnectionManager.GetHubContext<TaskHub>();
hubContext.Clients.All.onInsertTask(task);
Run Code Online (Sandbox Code Playgroud)

这有很多问题.首先,我希望客户端广播调用存在于单个类中,而不是直接从我的API控制器调用.其次,hubContext是一个实例,IHubContext而不是IHubCallerConnectionContext.这意味着我只能访问所有客户端,无法广播不同的响应Caller,Others就像我现在正在做的那样.

有没有办法真正从C#调用集线器的方法,理想情况下,可以访问不同的调用方选项?理想情况下,我可以从我的API控制器(或者更好的DI解决方案)中执行以下操作:

var taskHub = new TaskHub();
taskHub.InsertTask(task);
Run Code Online (Sandbox Code Playgroud)

提前致谢.

解

对于后代,根据Wasp的建议,我认为我会包含完整的解决方案.

首先,我修改了我的javascript(AngularJS)服务,将SignalR连接ID包含在API调用的自定义请求标头中,在本例中为INSERT:

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

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

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);
}
Run Code Online (Sandbox Code Playgroud)

我的中心看起来像这样我现在只使用从我的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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我在hub方法中有一个条件语句来处理是否未从我的应用程序的客户端部分启动集线器调用.如果外部应用程序/服务调用我的API,那就是这样.在这种情况下,SignalR连接和当然"ConnectionId"标头值将不存在.但在我的情况下,我仍然希望onInsertTask为所有连接的客户端调用该方法,以通知它们数据更改.这应该永远不会发生,但我只是为了完整而包含它.

Was*_*asp 7

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

也就是说,如果您的客户端在执行呼叫时已经将呼叫API(无论是Javascript还是C#)连接到集线器,您始终可以使用集线器的连接来装饰您对API的呼叫connectionId(通过查询字符串,通过标题,...).如果您的API收到该信息,则可以使用该模拟Caller API

Clients.Client(connectionId)
Run Code Online (Sandbox Code Playgroud)

它可以做同样的Others用

Clients.AllExcept(connectionId)
Run Code Online (Sandbox Code Playgroud)

在一个IHubContext实例上.查看官方文档.

然后,您可以按照DDan的建议IHubContext,以方便的集中方式封装使用,或者甚至重新构建它以使其易于符合DI标准.