【问题标题】:Getting Hub Context for ASPNet.Core Signal-R (.NET Core 2.1) RC获取 ASP Net.Core Signalr (.NET Core 2.1) RC 的集线器上下文
【发布时间】:2018-06-01 14:27:25
【问题描述】:

我正在使用 ASP.NET Core 2.1 RC1。 我也在使用 Signal-R(在此处找到):

https://docs.microsoft.com/en-us/aspnet/core/signalr/javascript-client?view=aspnetcore-2.1

我正在创建一个托管 Kestrel 并使用 Signal-R 的 .NET Core 控制台应用程序。我已经完全按照入门文档中的说明设置了 Startup。

这一切都很好。我能够连接到它,获取包含 signal-R 脚本的 HTML,接收我使用 Clients.All.SendAsync 制作的消息。效果很好。

但是 我希望能够从集线器外部向客户发送消息。我的应用程序中发生了一些事件,并向客户发送了一条消息。在完整的 .NET 中,我将使用 GlobalHost 并获取上下文。在 ALL 我对 Stack Overflow 的搜索中,它们引用了不再起作用的东西,或者在 IHubContext 中传递的 REST API 控制器中使用的东西。

我的 program.cs 中有一个事件监听器,当事件被触发时,我希望能够向我的 UserInterfaceHub 发送消息。

那么——我如何在 Program.CS 中获取集线器上下文——以便我可以从 Program.CS 中的事件委托中向它发送消息(调用 SwitchUI 方法)?

StartUp.cs

public void ConfigureServices(IServiceCollection services)  {
    services.Configure<CookiePolicyOptions>(options => {
        options.CheckConsentNeeded      = context => true;
        options.MinimumSameSitePolicy   = SameSiteMode.None;
    });
    services.AddMvc();

    services.AddCors(options => options.AddPolicy("CorsPolicy",
        builder =>  {builder.AllowAnyMethod().AllowAnyHeader().AllowAnyOrigin().AllowCredentials();}));
    services.AddSignalR();

    var provider = services.BuildServiceProvider();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
    if (env.IsDevelopment())    app.UseDeveloperExceptionPage();

    app.UseStaticFiles();
    app.UseCookiePolicy();
    app.UseCors("CorsPolicy");
    app.UseSignalR(routes => {routes.MapHub<UserInterfaceHub>("/uihub");});
    app.UseMvc();



    //app.Run(async (context) =>{await context.Response.WriteAsync("Active");});
}

Program.CS

    CreateWebHostBuilder(args)
        .UseKestrel()
        .UseUrls("http://0.0.0.0:" + appProperties.HostPort.ToString().Trim())
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .Build()
    .Start();

UserInterfaceHub.cs

namespace InterfaceModule.Hubs  {
    public class UserInterfaceHub : Hub  {
        public async Task SwitchUI(string message) {
            await Clients.All.SendAsync("ReceiveEvent", message);
        }

        public override async Task OnConnectedAsync()   {
            //await SwitchUI("HOWDY NEW PERSON!");
            await base.OnConnectedAsync();
        }
    }
}

编辑增加清晰度。

在 Program.CS 中,我有这个事件委托: //现在我们准备好了,开始听。 等待

deviceClient.SetInputMessageHandlerAsync(ModuleProperties.InputName, OnReceiveEvent, deviceClient);
            Console.WriteLine("INIT: Event Message Input handler created: [{0}]", ModuleProperties.InputName);

这是:

        static async Task<MessageResponse> OnReceiveEvent(Message message, object userContext)  {
//HOW DO I REACH THE HUB FROM HERE SO I CAN SEND A MESSAGE TO THE LISTENERS?
}

【问题讨论】:

    标签: asp.net-core signalr asp.net-core-2.0 asp.net-core-2.1


    【解决方案1】:

    我遇到了类似的情况,下面是解决方法:

    在您的服务层中,创建一个名为 ISendHubMessage 之类的接口。有一个名为 Send() 的方法,它接受您希望通过 SignalR 发送的参数。在同一个文件中创建一个名为 SendHubMessage 的类来实现该接口。让它返回。

    在您的顶级项目(您的 Startup.cs 文件所在的位置)中,创建另一个名为 SendHubMessage 的类,该类从您的服务层实现相同的 ISendHubMessage 接口。在此 SendHubMessage 中,您可以使用 DI 访问集线器,如上所述。此方法将执行通过 SignalR 发送的实际逻辑。

    在您的 Startup ConfigureServices() 方法中,添加以下行:

    services.AddTransient();

    (其中“Service”是您的服务级项目的命名空间,而命名空间中的“TopLevel”是您的顶级项目的命名空间)。

    您对这一行所做的事情是说“每当一个对象从服务层请求 ISendHubMessage 依赖项时,就向它提供在我的顶级项目中定义的 SendHubMessage 类”。

    最后,在您想要通过集线器发送消息的顶级项目之外的所有代码位置中,在构造函数中注入该 ISendHubMessage 依赖项。然后,您可以在类方法中引用它,当您调用 Send() 时,它将调用在您的顶级项目中的 SendHubMessage 类中定义的 Send() 方法。

    【讨论】:

      【解决方案2】:

      这行代码:

       app.UseSignalR(routes => {routes.MapHub<UserInterfaceHub>("/uihub");});
      

      会将您的集线器注册到 DI 容器。然后要访问它,您可以使用构造函数注入注入IHubContext&lt;UserInterfaceHub&gt;(例如在 Web 控制器中工作)或通过执行以下操作直接从 DI 容器访问它:

       var hub = app.ApplicationServices.GetRequiredService<IHubContext<UserInterfaceHub>>();
      

      (例如如果在 startup.cs Configure 方法中执行)

      如果您无法访问app.ApplicationServices,这基本上是您需要访问集线器的位置上的IServiceProvider,那么您将需要 1) 让该类与依赖注入一起工作以注入在IHubContext&lt;UserInterfaceHub&gt; 或IServiceProvider 中 2) 通过Configure 设置静态服务全局变量,以便您可以全局访问其中一个,或找到其他方式访问 DI 容器(又名 IServiceProvider)以获取您的通过上面的代码行集线器。

      拥有集线器后,将消息发送到注册客户端就像调用集线器上的方法一样简单。

       await hub.Clients.All.SendAsync("ReceiveEvent", message); 
      

      【讨论】:

      • 只是为了增加我的 2 美分,由于 ServiceLocator 反模式(通过构造函数传递 DI 容器),总是存在一些争议。您还可以传递 HttpContext 或 IHttpContextAccessor 并通过将返回 IServiceProvider 的上下文 (HttpContext.RequestServices) 访问您注册的服务。
      • @FranciscoTena 我同意使用 HttpContext.RequestServices 是一种很好的方法。当然,它仍然属于服务定位器反模式,但即使是假定的反模式,在经过深思熟虑的架构中也会时不时地占有一席之地。
      【解决方案3】:

      你的问题有点不清楚,但我假设你的意思是你想用可以通过你的集线器发送消息的东西替换以下内容:

      app.Run(async (context) =>{await context.Response.WriteAsync("Active");});
      

      由于这是在您的 Configure 方法中,您可以简单地将 IServiceCollection services 添加到您的 Configure 方法参数中。然后,你可以这样做:

      var hub = services.GetRequiredService<IHubContext<MyHub>>();
      

      但是,我不确定这最终会起到什么作用。在启动时,逻辑上你还没有订阅的客户。因此,此时通过您的集线器发送消息基本上无处可去。当用户真正点击您的网站并连接到您的中心时,您的应用程序的这一部分已经运行,不会再次被点击。

      【讨论】:

      • 最终,我想做的是从集线器类外部访问集线器的上下文。我在 Program.CS 中有一个方法来处理接收 Azure 事件中心事件。当 发生时,我想向 UserInterfaceHub 的 SwitchUI(或只是 Clients.All.SendAsyc)发送一条消息 - 但我看不到任何获取集线器上下文的方法。 GlobalHost 无法访问,所有旧方法都不起作用。我尝试添加您所说的内容,但没有任何 .GetRequiredService 在 Configure/ConfigureServices 中暴露自己,在方法参数中为其提供 IServiceCollection。没有看到它出现。
      • 基本上,我在 Program/Main 中定义了一个事件委托。当该事件被触发时,我想向所有收听我启动的 /eventUI 集线器的人发送信号-R 消息。如何获取该中心的上下文以便我可以与它“交谈”?
      • 对主要内容进行了一些编辑(见底部)。在 Main 中,我声明了一个事件委托处理程序。这是一个简单的异步方法——但从那里,我想向集线器发送一条消息——所以任何监听的客户端都会收到消息。如何连接到集线器的上下文以便我可以做到这一点?
      猜你喜欢
      • 2019-05-01
      • 1970-01-01
      • 2018-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-15
      • 1970-01-01
      • 2019-12-16
      相关资源
      最近更新 更多