Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Core 3 Worker Service Settings Dependency Injection

I have the following successfully working in .NET Core 2, but it doesn't work in .NET Core 3.

I've made a new Worker Service .Net Core 3 project from scratch and added only the minimum to recreate my error.

Here is my entry point in Program.cs

namespace WorkerService1DeleteMe
{
    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.Configure<ConfigChunk>(hostContext.Configuration.GetSection("ConfigChunk"));
                    services.AddHostedService<Worker>();
                });
    }

    public class ConfigChunk
    {
        public string Server { get; set; }
    }
}

So there is a single line added to CreateHostBuilder here, to the Configure method, and a new ConfigChunk class at the bottom, to match the schema of a section in appsettings.json.

Finally, an extension of the parameters to the Worker constructor to let it consume the SettingsChunk:

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly ConfigChunk _config;

    public Worker(ILogger<Worker> logger, ConfigChunk config)
    {
        _logger = logger;
        _config = config;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

As alluded to, this works fine in NET Core 2, but in 3 it fails with:

Unable to resolve service for type 'WorkerService1DeleteMe.ConfigChunk' while attempting to activate 'WorkerService1DeleteMe.Worker'.

I feel like I am missing something blindingly obvious but must confess I am at a loss. What I have observed is that I can make the whole thing work by going services.AddSingleton instead of services.Configure but I much prefer the pattern and reload functionality of the Configure approach if I can make it work.

like image 576
Adrian Hand Avatar asked Oct 08 '19 14:10

Adrian Hand


1 Answers

Few options are available.

With current configuration update worker to use IOptions<TOptions>

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly ConfigChunk _config;

    public Worker(ILogger<Worker> logger, IOptions<ConfigChunk> config) {
        _logger = logger;
        _config = config.Value;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
        while (!stoppingToken.IsCancellationRequested) {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

Or leave the worker as is and update the configuration to allow the class itself to be injected.

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) => {
                services.Configure<ConfigChunk>(hostContext.Configuration.GetSection("ConfigChunk"));
                services.AddHostedService<Worker>();
                services.AddTransient<ConfigChunk>(_ => _.GetRequiredService<IOptions<ConfigChunk>>().Value);

            });

The second example will allow for explicit injection of the ConfigChunk while still allowing the options features to be in effect and does not tightly couple the worker to framework concerns.

like image 105
Nkosi Avatar answered Sep 23 '22 03:09

Nkosi