从Controller调用SignalR Core Hub方法

Mak*_*kla 39 c# signalr asp.net-core-2.0 asp.net-core-signalr

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

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

我的控制器:

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

我的中心:

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

Ste*_*phu 58

解决方案1

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

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

private IHubContext<VarDesignHub> HubContext
{ get; set; }
Run Code Online (Sandbox Code Playgroud)

然后你也可以打电话

await this.HubContext.Clients.All.InvokeAsync("Completed", id);
Run Code Online (Sandbox Code Playgroud)

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

解决方案2

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

public interface ITypedHubClient
{
    Task BroadcastMessage(string name, string message);
}
Run Code Online (Sandbox Code Playgroud)

从Hub继承:

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

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

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

  • 但有没有办法从控制器调用.Send方法.这种依赖注入似乎有点无用.理想情况下,我想在我的Hub类中设置消息名称,而不必在一堆不同的地方定义它们.但要做到这一点,我需要能够在集线器中调用Send方法 (8认同)
  • 这个答案很棒.谢谢,但它并没有真正回答我的问题.它现在会这样做.但迟早我需要调用Hub方法.不向所有客户发送消息. (5认同)
  • @MarkRedman我没有,没有.我最终做的是将Hub上下文包装在服务中,比如HubService,然后为不同的调用方法构建一个接口.至少它将它保存在一个地方. (4认同)

小智 23

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

简单的答案是您不能直接从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();
}
Run Code Online (Sandbox Code Playgroud)

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

services.AddSingleton<IDoStuff, DoStuff>();
Run Code Online (Sandbox Code Playgroud)

完整的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<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?}");
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class MyHub : Hub
{
    private readonly IDoStuff _doStuff;

    public MyHub(IDoStuff doStuff)
    {
        _doStuff = doStuff;
    }

    public string GetData()
    {
       return  _doStuff.GetData();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • 因此,我们是否必须从 Controller 和 Hub 调用 Clients.All 方法?这似乎是多余的,我也想知道如何调用客户端方法。我们应该像平常那样在 Hub 中称呼它们吗? (2认同)

Hig*_*gty 7

另一个不使用注入的答案在这里。

我设计了我的中心类,如下所示。

public class NotificationHub : Microsoft.AspNetCore.SignalR.Hub
{
    public static IHubContext<NotificationHub> Current { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在你的创业班

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    NotificationHub.Current = app.ApplicationServices.GetService<IHubContext<NotificationFromServerHub>>();

}
Run Code Online (Sandbox Code Playgroud)

所以,你可以从任何地方像这样使用。

public class MyBizClass
{
    public void DoSomething()
    {
        NotificationHub.Current.MyMethod(...);
    }
}
Run Code Online (Sandbox Code Playgroud)


DrS*_*an1 5

现在这里有充分的证明

您可以将IHubContext实例添加到控制器中,方法是将其添加到构造函数中:

public class HomeController : Controller
{
    private readonly IHubContext<NotificationHub> _hubContext;

    public HomeController(IHubContext<NotificationHub> hubContext)
    {
        _hubContext = hubContext;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,通过访问IHubContext实例,您可以像调用集线器本身一样调用集线器方法。

public async Task<IActionResult> Index()
{
    await _hubContext.Clients.All.SendAsync("Notify", $"Home page loaded at: {DateTime.Now}");
    return View();
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

30130 次

最近记录:

6 年,4 月 前