Внедрение SignalR в Core WorkerService

Я пытаюсь внедрить SignalR в проект WorkerService, а затем имитировать уведомления на стороне сервера.

Далее в моем файле Worker.cs я получаю несколько нулевых ссылок, что говорит мне о том, что я изначально не являюсь должным образом своим объектом Hub.

Сначала я создал NotificationHub solution с одноименным проектом.

Вот Notifications.cs - отлично работает.

using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NotificationHub.Hubs
{
    public interface IHubClient
    {
        Task MessageReceived(string user, string message);
        Task MessageReceived(string message);
    }

    public class Notifications: Hub<IHubClient>
    {
        [HubMethodName("SendMessageToClient")]
        public async Task SendMessage(string username, string message)
        {
            await Clients.All.MessageReceived(username, "from Notification Hub:" + message);
        }
        
        public override Task OnConnectedAsync()
        {
            Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
            _ = SendMessage("From Server", "Hub reconnected on server.");
            return base.OnConnectedAsync();
        }

        public override Task OnDisconnectedAsync(Exception exception)
        {
            Console.Write("Hub just disconnected on server.");
            return base.OnDisconnectedAsync(exception);
        }
    }
}

NotificationHub работает в IIS на моем локальном компьютере, что позволяет мне вызывать Notification Hub из моего TypeScript клиента.

то есть я просто добавил простой HTML button для запуска сообщения от клиента. Моя функция срабатывает:

   this.hubConnection.send('SendMessageToClient', 'client', `Message from client side. ${today}`)
                    .then(() => console.log('Message sent from client.'));

введите здесь описание изображения

Core WorkerService: моя проблема здесь

Затем я добавил NotificationWorkService, в который я пытаюсь внедрить SignalR. Цель состоит в том, чтобы в конечном итоге эта служба работала на сервере Windows, который регулярно проверяет наличие уведомлений и доставляет их в браузер.

В моем проекте WorkerService вот Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace HarmonyNotificationWorkerService
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker>();
                });
    }
}

а вот Worker.cs -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Client;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NotificationHub.Hubs;

namespace HarmonyNotificationWorkerService
{
    public interface IHubClient
    {
        Task MessageReceived(string user, string message);
        Task MessageReceived(string message);
    }

    public class Worker : BackgroundService
    {
        private IHubContext<Notifications> _hubContext;
        private readonly Hub<IHubClient> _hubNotif;
        private readonly ILogger<Worker> _logger;
       
        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            using (var hubConnection = new HubConnection("/hub"))
            {
                IHubProxy hubproxy = hubConnection.CreateHubProxy("https://localhost:44311/hub");

                await hubproxy.Invoke("SendMessage", "test");
            }
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
              
                await Task.Delay(1000, stoppingToken);
            }
        }

        private async Task CheckForNotifications()
        {                      
            await _hubNotif.Clients.All.MessageReceived("from work server", "Dude, you've got mail!");
        }
    }
}

ПРОБЛЕМА:

Проблема в том, что объекты _hubContext и _hubNotif всегда нулевые, поэтому я не могу проверить ни один из них. Я изначально не понимаю их правильно, но я не уверен, как это сделать. С# также показывает мне предупреждение об этой нулевой ссылке.

System.InvalidOperationException: «Данные не могут быть отправлены, так как соединение находится в отключенном состоянии. Запуск вызова перед отправкой любых данных.`

ОБНОВЛЕНИЕ: УСТАНОВИТЕ КЛИЕНТСКИЙ ПАКЕТ SIGNALR

Я добавил using Microsoft.AspNetCore.SignalR.Client; к Worker.cs и установил private readonly HubConnection _hubConnection; вверху класса Worker.

Затем я изменил следующий метод, чтобы проверить состояние подключения, а затем отправить сообщение:

 private async Task CheckForNotifications()
        {
            if (_hubConnection.State == HubConnectionState.Disconnected)
            {
                await _hubConnection.StartAsync();
            }            
            _ = _hubConnection.SendAsync("SendMessageToClient", "from service worker", "You've got mail!");
        }

Вроде все успешно работает.

Поток действительно завершается с code 0 время от времени, но я считаю, что в данном случае это нормально, так как я работаю на Task.Delay(5000);


person bob.mazzo    schedule 25.06.2020    source источник
comment
Сообщение об ошибке точно указывает, что делать. Call start before sending any data. т.е. await hubConnection.Start();   -  person Nkosi    schedule 26.06.2020
comment
Не смешивайте ASP.NET SignalR и ASP.NET Core SignalR. Они несовместимы друг с другом. Ваш работник должен использовать HubConnectionBuilder для получения соединения и отправки сообщений с его помощью. docs.microsoft.com/aspnet/core/signalr/   -  person Brennan    schedule 26.06.2020
comment
@Nkosi - в моем объекте-концентраторе нет метода Start(). Вероятно, это потому, что я должен был использовать Microsoft.AspNetCore.SignalR.Client в своем сервисе, подобно тому, как я использую '@microsoft/signalr' в TypeScript для своего браузерного приложения Angular.   -  person bob.mazzo    schedule 26.06.2020
comment
@ Бреннан - да, я где-то это читал, но выбрал неправильный подход. Теперь с клиентским пакетом я могу установить соединение с концентратором и отправить два сообщения из моей службы в концентратор, а затем клиент моего браузера сможет обработать событие messageReceived. Однако мой сервис вылетает через несколько секунд с ошибкой. Сначала вы видите, что мой _logger работает нормально, затем thread has exited with code 0 ==› NotificationWorkerService.Worker: Information: Worker running at: 06/26/2020 11:08:32 -04:00 Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll   -  person bob.mazzo    schedule 26.06.2020
comment
@Brennan - я добавил обновление в конец своего поста. Работает несколько секунд и вылетает. Я включил выходное сообщение. Возможно, для решения остальных моих вопросов необходим новый пост. Спасибо.   -  person bob.mazzo    schedule 26.06.2020
comment
Я могу только догадываться, поскольку ваш код не завершен, но вы, вероятно, вызываете CheckForNotifications несколько раз, и, поскольку вы включили туда StartAsync, он попытается запуститься снова и сбросить.   -  person Brennan    schedule 26.06.2020
comment
@ Бреннан - я понял. Я добавил тестовый код в Program.cs под IHostBuilder CreateHostBuilder, что вызывало исключение. Я сожалею о том, что. Кажется, теперь все работает. Я также проверяю состояние if (_hubConnection.State == HubConnectionState.Disconnected), что делает его чище. Можете ли вы добавить свой комментарий в качестве ответа? Я счастлив выбрать его.   -  person bob.mazzo    schedule 26.06.2020


Ответы (1)


Не смешивайте ASP.NET SignalR и ASP.NET Core SignalR. Они несовместимы друг с другом. Ваш сервер использует ASP.NET Core SignalR, поэтому ваш рабочий должен создать HubConnection с помощью файла HubConnectionBuilder.

https://docs.microsoft.com/aspnet/core/signalr/dotnet-client?view=aspnetcore-3.1&tabs=visual-studio#connect-to-a-hub.

person Brennan    schedule 26.06.2020
comment
Мой WorkerService теперь использует класс HubConnection из Microsoft.AspNetCore.SignalR.Client; - person bob.mazzo; 29.06.2020