Несколько реализаций IHostedService - PullRequest
0 голосов
/ 09 октября 2018

Я пытаюсь создать фоновые сервисы, используя IHostedService.Все работает нормально, если у меня только один фоновый сервис.Когда я пытаюсь создать более одной реализации IHostedService, на самом деле запускается только та, которая была зарегистрирована первой.

services.AddSingleton<IHostedService, HostedServiceOne>();
services.AddSingleton<IHostedService, HostedServiceTwo>();

В приведенном выше примере вызывается StartAsync on HostedServiceOne, но StartAsync onHostedServiceTwo никогда не вызывается.Если я поменяю порядок регистрации двух реализаций IHostedService (поместите IHostedServiceTwo перед IHostedServiceOne), то вызовется StartAsync на HostedServiceTwo, но никогда не будет HostedServiceOne.

РЕДАКТИРОВАТЬ:

Меня направили на следующее:

Как зарегистрировать несколько реализаций одного и того же интерфейса в Asp.Net Core?

Однако это не такдля IHostedService.Чтобы использовать предложенный подход, мне нужно было бы позвонить на serviceProvider.GetServices<IService>();, но кажется, что IHostedService.StartAsync, кажется, вызывается изнутри.Я даже не уверен, где бы я это назвал, чтобы вызвать IHostedService.StartAsync.

Ответы [ 6 ]

0 голосов
/ 18 июня 2019

Я столкнулся с немного другой проблемой, однако решение для нее может быть применимо и здесь.Мне нужно было зарегистрировать реализацию сервиса как синглтон и запустить его как IHostedService.Основываясь на решении Хавьера Капелло, я придумал что-то вроде этого:

public class HostedServiceDecorator<T> : IHostedService where T : IHostedService
{
    private readonly IHostedService _next;

    public HostedServiceDecorator(T target)
    {
        _next = target;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        if (_next != null)
        {
            await _next.StartAsync(cancellationToken);
        }
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        if (_next != null)
        {
            await _next.StopAsync(cancellationToken);
        }
    }
}

И тогда я смог бы сделать то, что мне нужно:

        services.AddSingleton<INetworkStatusService, NetworkStatusService>();
        services.AddHostedService<HostedServiceDecorator<INetworkStatusService>>();

И чтобы ответить на вопрос, можно сделать это:

        services.AddTransient<NetworkStatusService>();
        services.AddHostedService<HostedServiceDecorator<NetworkStatusService>>();
0 голосов
/ 18 апреля 2019

Оставайтесь асинхронными!как обсуждено здесь ...

https://github.com/aspnet/Hosting/issues/1560

protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{            
    while (!stoppingToken.IsCancellationRequested)
    {
        // Do something nice here
        await Task.Delay(2000);
    }
}   
0 голосов
/ 07 февраля 2019

У меня была такая же проблема.Пришлось возвращать Task.CompletedTask в каждой службе;

public class MyHostedService: IHostedService
{
    public Task StartAsync(CancellationToken cancellationToken)
    {
        Task.Run(() => SomeInfinityProcess(cancellationToken));
        return Task.CompletedTask;
    }

    public void SomeInfinityProcess(CancellationToken cancellationToken)
    {
        for (; ; )
        {
            Thread.Sleep(1000);
            if (cancellationToken.IsCancellationRequested)
                break;
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

Startup.cs такой же:

    services.AddHostedService<MyHostedService>();
    services.AddHostedService<MyHostedService2>();
    ...
0 голосов
/ 17 октября 2018

У меня была та же проблема, когда несколько реализаций IHostedService не вызывались, только первая.Убедитесь, что вы используете метод TryAdd в методе IWebHostBuilder.ConfigureServices () вместо метода Add.Первое включение не допускает дублирование реализаций интерфейсов, второе -.Это исправило это для меня.

Так что используйте это:

webHost = WebHost
            .CreateDefaultBuilder()
            .ConfigureServices(x => x.Add(serviceCollection))

Вместо этого:

webHost = WebHost
            .CreateDefaultBuilder()
            .ConfigureServices(x => x.TryAdd(serviceCollection))
0 голосов
/ 09 октября 2018

Зарегистрируйте свой HostedService как показано ниже:

// services.AddSingleton<IHostedService, HostedServiceOne>();
// services.AddSingleton<IHostedService, HostedServiceTwo>();

services.AddHostedService<HostedServiceOne>();
services.AddHostedService<HostedServiceTwo>();

[Обновление]:

См. Комментарии ниже @nickvane:

Этопотому что первая зарегистрированная служба не возвращает Task для метода StartAsync, поэтому среда выполнения ожидает и не выполняет StartAsync следующего зарегистрированного экземпляра hostedservice

Вероятно, первая StartAsync() didn 'т финиш

0 голосов
/ 09 октября 2018

Похоже, что это можно решить с помощью украшения IHostedService, хотя контейнер по умолчанию .Net Core IoC не поддерживает регистрацию декораторов, для этого есть простой обходной путь.

Вы можете создать декоратор для IHostedService следующим образом:

public abstract class MyHostedServiceDecorator : IHostedService
{
    private readonly MyHostedServiceDecorator _next;

    protected MyHostedServiceDecorator(MyHostedServiceDecorator next)
    {
        _next = next;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        await StartAsyncInternal(cancellationToken);

        if (_next != null)
        {
            await _next.StartAsync(cancellationToken);
        }
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        await StopAsyncInternal(cancellationToken);

        if (_next != null)
        {
            await _next.StopAsync(cancellationToken);
        }
    }

    protected abstract Task StartAsyncInternal(CancellationToken token);

    protected abstract Task StopAsyncInternal(CancellationToken token);
}

Создайте столько декораторов, сколько вам нужно:

public class HostedServiceOneDecorator : MyHostedServiceDecorator
{
    public HostedServiceOneDecorator(MyHostedServiceDecorator next) : base(next)
    {
    }

    protected override async Task StartAsyncInternal(CancellationToken token)
    {
        Console.Write("This is my decorated start async!");
    }

    protected override async Task StopAsyncInternal(CancellationToken token)
    {
        Console.Write("This is my decorated stop async!");
    }
}

В вашем зарегистрированном сервисном вызове на хостингедекоратор выглядит так:

public class MyHostedService : IHostedService
{
    private readonly MyHostedServiceDecorator
        _decorator;

    public MyHostedService(MyHostedServiceDecorator decorator)
    {
        _decorator = decorator;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        // base StartAsync logic ...
        await _decorator.StartAsync(cancellationToken);
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        // base StopAsync logic ...
        await _decorator.StopAsync(cancellationToken);
    }
}

И, наконец, вы регистрируете сервис и его декораторы, передавая следующий декоратор в предыдущий конструктор.

services.AddSingleton<IHostedService, MyHostedService>();

services.AddSingleton<MyHostedServiceDecorator>(
    new HostedServiceOneDecorator(new HostedServiceTwoDecorator(/*etc*/)));

Все декораторы будут вызваны вЦепная мода, пока нет следующей!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...