从Controller调用SignalR Core Hub方法

如何从Controller调用SignalR Core Hub方法?
我使用ASP.NET Core 2.0与Microsoft.AspNetCore.SignalR(1.0.0-alpha2-final)。

我有与Excel,SolidEdge通信的Windows服务…当操作完成后,它会在ASP.NET Core应用程序中向我的控制器发送请求。 现在我需要通过SignalR通知连接到服务器的所有客户端外部程序完成了一些任务。
我无法改变窗口服务的工作方式。 (无法从窗口服务连接到SignalR)。
我为旧的SignalR(GlobalHost.ConnectionManager.GetHubContext)找到了很多解决方案,但是已经发生了很多变化,这些解决方案不再适用了。

我的控制器:

 [Route("API/vardesigncomm")] public class VarDesignCommController : Controller { [HttpPut("ProcessVarDesignCommResponse/{id}")] public async Task ProcessVarDesignCommResponse(int id) { //call method TaskCompleted in Hub !!!! How? return new JsonResult(true); } } 

我的中心:

 public class VarDesignHub : Hub { public async Task TaskCompleted(int id) { await Clients.All.InvokeAsync("Completed", id); } } 

解决方案1

另一种可能性是将HubContext注入您的控制器,如:

 public VarDesignCommController(IHubContext hubcontext) { HubContext = hubcontext; ... } private IHubContext HubContext { get; set; } 

然后你也可以打电话

 await this.HubContext.Clients.All.InvokeAsync("Completed", id); 

但是,您将在所有客户端上直接调用方法。

解决方案2

您还可以使用类型化集线器:简单创建一个接口,您可以在其中定义服务器可以在客户端上调用的方法:

 public interface ITypedHubClient { Task BroadcastMessage(string name, string message); } 

从Hubinheritance:

  public class ChatHub : Hub { public void Send(string name, string message) { Clients.All.BroadcastMessage(name, message); } } 

将键入的hubcontext注入控制器,然后使用它:

 [Route("api/demo")] public class DemoController : Controller { IHubContext _chatHubContext; public DemoController(IHubContext chatHubContext) { _chatHubContext = chatHubContext; } // GET: api/values [HttpGet] public IEnumerable Get() { _chatHubContext.Clients.All.BroadcastMessage("test", "test"); return new string[] { "value1", "value2" }; } } 

目前的答案没有回答提出的问题。

简单的答案是您不能直接从MVC控制器或其他地方调用集线器方法。 这是设计的。 可以将集线器视为包含SignalR Core客户端要调用的端点,而不是服务器或控制器方法。

这就是微软所说的 (这是SignalR之前的Core文档,但它仍然适用于SignalR Core):

您没有实例化Hub类或从服务器上自己的代码调用其方法; SignalR Hubs管道为您完成的所有工作。 每次需要处理Hub操作时,SignalR都会创建Hub类的新实例,例如客户端连接,断开连接或对服务器进行方法调用时。

因为Hub类的实例是瞬态的,所以不能使用它们来维持从一个方法调用到下一个方法调用的状态。 每次服务器从客户端接收方法调用时,Hub类的新实例都会处理该消息。 要通过多个连接和方法调用来维护状态,请使用其他方法,例如数据库,Hub类上的静态变量,或不从Hub派生的其他类。 如果将数据保留在内存中,使用Hub类上的静态变量等方法,则应用程序域回收时数据将丢失。

如果要从在Hub类外部运行的自己的代码向客户端发送消息,则不能通过实例化Hub类实例来实现,但可以通过获取Hub类的SignalR上下文对象的引用来实现。 …

如果集线器中存在需要调用的代码,最好将其放入可从任何位置访问的外部类或服务中。

这是一个使用ASP.NET Core的简单内置DI框架的示例:

假设您需要调用的代码在DoStuff.cs中:

 public class DoStuff : IDoStuff { public string GetData() { return "MyData"; } } public interface IDoStuff { string GetData(); } 

在Startup.cs中,使用内置容器配置单例:

  services.AddSingleton(); 

完整的Startup.cs如下所示:

 public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddSignalR(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddSingleton(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseSignalR(routes => { routes.MapHub("/myhub"); }); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } 

对于您的集线器类,注入单例,并在方法中使用它:

 public class MyHub : Hub { private readonly IDoStuff _doStuff; public MyHub(IDoStuff doStuff) { _doStuff = doStuff; } public string GetData() { return _doStuff.GetData(); } } 

然后在你的控制器中,注入IHubContext和单例:

 public class HomeController : Controller { private readonly IDoStuff _doStuff; private readonly IHubContext _hub; public HomeController(IDoStuff doStuff, IHubContext hub) { _doStuff = doStuff; _hub = hub; } public async Task Index() { var data = _doStuff.GetData(); await _hub.Clients.All.SendAsync("show_data", data); return View(); } } 

当然,您的Javascript或其他客户端应该配置show_data回调。

请注意,我们使用注入的集线器上下文将数据发送到所有SignalR客户端:_hub.Clients.All.SendAsync(…)