Концентратор SignalR разрешает значение null внутри обработчика подписки RabbitMQ в ASP.NET Core

У меня есть проект ASP.NET Core MVC с RabbitMQ (посредством EasyNetQ) и SignalR.

Далее у меня есть подписка на сообщение RabbitMQ, которое должно отправлять уведомление клиенту через SignalR.

Но, к сожалению, концентратор всегда разрешает null.

Интересным наблюдением является то, что когда приложение все еще запускается и в очереди все еще есть неподтвержденные сообщения, служба на самом деле разрешается просто отлично.

public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();
    services.RegisterEasyNetQ("host=localhost;virtualHost=/");
}

public void Configure(IApplicationBuilder app)
{
    app.UseSignalR(route =>
    {
        route.MapHub<MyHub>("/mypath");
    });

    app.Use(async (context, next) =>
    {
        var bus = context.RequestServices.GetRequiredService<IBus>();

        bus.SubscribeAsync<MyMessage>("MySubscription", async message =>
        {
            var hubContext = context.RequestServices
                .GetRequiredService<IHubContext<MyHub>>();

            // hubContext is null 
            await hubContext.Clients.All.SendAsync("MyNotification");
        });

        await next.Invoke();
    });
}

Я подозреваю, что, возможно, я делаю что-то неправильно в отношении регистрации подписки внутри app.Use, но я не могу найти никаких полезных примеров, так что это было лучшее, что я мог понять.

Я использую предварительную версию ASP.NET Core 3 5, я не знаю, имеет ли это какое-либо отношение к моей проблеме.

Итак, вопрос: как мне получить контекст концентратора внутри обработчика подписки на сообщения?

ОБНОВЛЕНИЕ

Я проверил GetRequiredService docs, и вызов должен фактически выдать InvalidOperationException, если служба не может быть разрешена, но это не так. Он возвращает null, что, насколько я могу судить, невозможно (если только контейнер по умолчанию не поддерживает регистрацию экземпляров с нулевым значением).


person Sandor Drieënhuizen    schedule 29.05.2019    source источник


Ответы (1)


Мне удалось решить проблему с помощью этой проблемы, реализуя вместо этого IHostedService.

public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();
    services.RegisterEasyNetQ("host=localhost;virtualHost=/");
    services.AddHostedService<MyHostedService>();
}

public void Configure(IApplicationBuilder app)
{
    app.UseSignalR(route =>
    {
        route.MapHub<MyHub>("/mypath");
    });    
}

public class MyHostedService : BackgroundService
{
    private readonly IServiceScopeFactory _serviceScopeFactory;

    public ServiceBusHostedService(IServiceScopeFactory serviceScopeFactory)
    {
        _serviceScopeFactory = serviceScopeFactory;
    }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var scope = _serviceScopeFactory.CreateScope();
        var bus = scope.ServiceProvider.GetRequiredService<IBus>();

        bus.SubscribeAsync<MyMessage>("MySubscription", async message =>
        {
            var hubContext = scope.ServiceProvider.GetRequiredService<IHubContext<MyHub>>();

            await hubContext.Clients
                .All
                .SendAsync("MyNotification", cancellationToken: stoppingToken);
        });

        return Task.CompletedTask;
    }
}
person Sandor Drieënhuizen    schedule 29.05.2019
comment
Привет, у меня есть вопрос, почему нам не нужно помещать цикл While или обратный вызов таймера в ExecuteAsync для bus.SubscribeAsync‹›(); ? заранее спасибо. - person Armin Shoeibi; 16.05.2021
comment
@Armin Shoeibi: поскольку служба зарегистрирована как размещенная служба, ей не потребуется цикл, чтобы предотвратить остановку приложения. Подписка на сообщение шины представляет собой обратный вызов, который выдается EasyNetQ. - person Sandor Drieënhuizen; 18.05.2021