type signal example c# signalr asp.net-core-2.0 asp.net-core-signalr

example - signalr server c#



Método de concentrador de llamada de SignalR desde el controlador (2)

La respuesta actual no responde a la pregunta planteada.

La respuesta simple es que no puede llamar directamente a un método de hub desde un controlador MVC o desde otro lugar. Esto es por diseño. Piense que el concentrador contiene los puntos finales para los clientes de SignalR Core, no para los métodos del servidor o del controlador.

Esto es lo que says Microsoft (esto es documentación previa a SignalR Core, pero aún se aplica a SignalR Core):

No crea una instancia de la clase Hub o llama a sus métodos desde su propio código en el servidor; todo lo que se hace por usted a través de la tubería SignalR Hubs. SignalR crea una nueva instancia de su clase de Hub cada vez que necesita manejar una operación de Hub como cuando un cliente se conecta, desconecta o realiza una llamada de método al servidor.

Debido a que las instancias de la clase Hub son transitorias, no puede usarlas para mantener el estado de una llamada de método a la siguiente. Cada vez que el servidor recibe una llamada de método de un cliente, una nueva instancia de su clase Hub procesa el mensaje. Para mantener el estado a través de múltiples conexiones y llamadas de método, use algún otro método como una base de datos, una variable estática en la clase Hub o una clase diferente que no se derive de Hub. Si conserva los datos en la memoria, utilizando un método como una variable estática en la clase Hub, los datos se perderán cuando el dominio de la aplicación se recicle.

Si desea enviar mensajes a los clientes desde su propio código que se ejecuta fuera de la clase Hub, no puede hacerlo creando una instancia de la clase Hub, pero puede hacerlo obteniendo una referencia al objeto de contexto SignalR para su clase Hub ...

Si hay un código en el concentrador al que debe llamar, es mejor ubicarlo en una clase o servicio externo al que se pueda acceder desde cualquier lugar.

Así que aquí hay un ejemplo que utiliza el sencillo marco integrado de DI para ASP.NET Core:

Suponiendo que el código que necesita llamar está en DoStuff.cs:

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

En Startup.cs, configure un singleton usando el contenedor incorporado:

services.AddSingleton<IDoStuff, DoStuff>();

El Startup.cs completo se ve así:

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<CookiePolicyOptions>(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<IDoStuff, DoStuff>(); } // 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>("/myhub"); }); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } }

Para su clase de hub, inyecte el singleton y utilícelo en un método:

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

Luego, en su controlador, inyecte el IHubContext y el singleton:

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

Por supuesto, su Javascript u otro cliente deben tener configurada una devolución de llamada show_data.

Tenga en cuenta que estamos utilizando el contexto del concentrador inyectado para enviar los datos a todos los clientes de SignalR: _hub.Clients.All.SendAsync (...)

¿Cómo puedo llamar al método SignalR Core Hub desde el controlador?
Estoy usando ASP.NET Core 2.0 con Microsoft.AspNetCore.SignalR (1.0.0-alpha2-final).

Tengo un servicio de Windows que se comunica con Excel, SolidEdge ... Cuando se completa la operación, se envía una solicitud a mi controlador en la aplicación Core de ASP.NET. Ahora debo informar a todos los clientes conectados al servidor con SignalR que el programa externo completó alguna tarea.
No puedo cambiar la forma en que funciona el servicio de ventanas. (No se puede conectar a SignalR desde el servicio de ventana).
Encontré muchas soluciones para el viejo SignalR ( GlobalHost.ConnectionManager.GetHubContext ), pero muchas cosas han cambiado y esas soluciones ya no funcionan.

Mi controlador

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

Mi hub:

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


Solución 1

Otra posibilidad es inyectar su HubContext en su controlador como:

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

Entonces también puedes llamar

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

Pero luego dirigirá los métodos de llamada a todos los clientes.

Solucion 2

También puede trabajar con concentradores escritos: cree una interfaz simple donde defina a qué métodos puede llamar su servidor en los clientes:

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

Heredar de Hub:

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

Inyecte su hubcontext escrito en su controlador y trabaje con él:

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