3

I know prior to Asp.Net 3.0 (or 3.1), to manually start a BackgroundService, we could derive it from IHostedService instead and change the DI registration to:

services.AddSingleton<IHostedService, CustomHostedService>();

and then manually trigger the service start by injecting the service in the constructor, and calling StartAsync().

However, I can't seem to do that in Asp.Net Core 3.1. Having a look at the code for the StartAsync() method, background services are started before the app startup is completed.

public async Task StartAsync(CancellationToken cancellationToken = default)
{
    _logger.Starting();
    await _hostLifetime.WaitForStartAsync(cancellationToken);

    cancellationToken.ThrowIfCancellationRequested();
    _hostedServices = Services.GetService<IEnumerable<IHostedService>>();

    foreach (var hostedService in _hostedServices)
    {
        // Fire IHostedService.Start
        await hostedService.StartAsync(cancellationToken).ConfigureAwait(false);
    }

    // Fire IApplicationLifetime.Started
    _applicationLifetime?.NotifyStarted();

    _logger.Started();
}

What's the best way to manually trigger a background service to start?

Essentially, this is my setup:

Background Service

public MyBackgroundService(Func<string, IConsumer> consumerFactory)
{
    _consumerFactory = consumerFactory ?? throw new ArgumentNullException(nameof(consumerFactory));
}

Consumer factory registration

services.AddSingleton<Func<string, IConsumer>>(provider => providerName => provider.ConfigureConsumer(environment, providerName));

private static IConsumer ConfigureConsumer(this IServiceProvider provider, IHostEnvironment environment, string provideName)
{
    if (string.IsNullOrEmpty(provideName))
        throw new ArgumentNullException(nameof(provideName));

    var options = provider.GetRequiredService<IOptions<ConsumerConfig>>();
    if (options?.Value == null)
        throw new ArgumentNullException(nameof(options));

    return environment.IsDevelopment() 
        ? new Consumer(options, provider.GetTopics()) 
        : new Consumer((IOptions<ConsumerConfig>)options.SetSecurityOptions(provider), provider.GetTopics());
}

private static IOptions<Config> SetSecurityOptions(this IOptions<Config> config, IServiceProvider provider)
{
    var certService = provider.GetRequiredService<IVaultCertificateService>();
    ...
    return config;
}

Essentially this certService properties are only set when a request comes in and executes a middleware. And since when ExecuteAsync() in the background service tries to get an instance of consumer from the factory, is executed regardless, I dont have the IConsumer properly configured Thanks

4

1 回答 1

4

注入的托管服务从应用程序开始,然后停止。框架不为您提供手动控制。应该有这样的根本原因。当然,您可以使用线程实现自己的解决方案,但如果您希望继续使用 Microsoft 的内置解决方案,您可以使用Queued Background Task。您将使用空任务队列启动后台服务。服务将监听您要添加到队列中的第一个任务。您可以随时添加任务,然后它将在后台运行。

于 2020-04-08T06:46:13.847 回答