How to setup app settings in a .Net Core 3 Worker Service
Asked Answered
V

3

81

I have been looking at a number of tutorials and SO questions (such as App Settings .Net Core) regarding reading appsettings.json in .Net Core 3 and I cannot find any pointers on how-to when dealing with the Worker service. There is no Startup method. Instead, I have a Program.cs with the main method:

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>();
            });
}

How do I go about reading from the appsettings.json file in a Worker Service Project in .Net Core 3?

I have added a reference to a custom WCF client I created with .Net v4.8 and another project that has just all the Busines Domain Object shared between the whole solution. My solution is primarily .Net v4.8 and I want to use the Worker Service. The Client project creates a WCF Client internally by code so all binding and endpoints are configurable. If this were a .Net v4.8 project, I'd add the following to the app.config:

<appSettings>
    ...
    <add key="AminServiceUri" value="http://localhost:45108/ServiceHost/v1/AminService.svc" />
    <add key="BillServiceUri" value="http://localhost:45108/ServiceHost/v1/BillService.svc" />
    <add key="CustomerServiceUri" value="http://localhost:45108/ServiceHost/v1/CustomerService.svc" />
    <add key="EpayServiceUri" value="http://localhost:45108/ServiceHost/v1/EpayService.svc" />
    <add key="FinanceServiceUri" value="http://localhost:45108/ServiceHost/v1/FinanceService.svc" />
    <add key="GrpServiceUri" value="http://localhost:45108/ServiceHost/v1/GrpService.svc" />
    <add key="MetaServiceUri" value="http://localhost:45108/ServiceHost/v1/MetaService.svc" />
    <add key="ReportServiceUri" value="http://localhost:45108/ServiceHost/v1/ReportService.svc" />
    <add key="ServiceInfoServiceUri" value="http://localhost:45108/ServiceHost/v1/ServiceInfoService.svc" />
    <add key="UsersServiceUri" value="http://localhost:45108/ServiceHost/v1/UsersService.svc" />
    ...
    <add key="ExcessiveLogging" value="false" />
    ...
</appSettings>

Now I need these settings to be in the new JSON format and read them in.

Edit

This is a fresh project. The worker is not doing anything:

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> logger;

    public Worker(ILogger<Worker> logger)
    {
        this.logger = logger;
    }

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

And here is how I get this project type:

Worker Service

Violist answered 1/10, 2019 at 11:31 Comment(0)
L
156

If for example the worker class needed access to some data stored in your appsettings

public class Worker : BackgroundService {
    private readonly ILogger<Worker> logger;
    private readonly WorkerOptions options;

    public Worker(ILogger<Worker> logger, WorkerOptions options) {
        this.logger = logger;
        this.options = options;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
        while (!stoppingToken.IsCancellationRequested) {
            //do something that uses options

            logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(5000, stoppingToken);
        }
    }
}

Where WorkerOptions stores your values from configuration.

public class WorkerOptions {
    public string AminServiceUri { get; set; }
    public string BillServiceUri { get; set; }

    //... other properties
}

Which assumes the appsettings.json file has the corresponding keys

{
  "WCF": {
    "AminServiceUri":"http://localhost:45108/ServiceHost/v1/AminService.svc",
    "BillServiceUri":"http://localhost:45108/ServiceHost/v1/BillService.svc",

    //...other key-value pairs
  },
  "Logging": {
    "ExcessiveLogging": false
  }

}

By default Host.CreateDefaultBuilder will set up the usual configuration (appsettings.json et al).

Use hostContext.Configuration to get the IConfiguration instance that can be used to access the desired settings and add the strongly typed object model for it. Add that object to the service collection so that it can be injected where needed

For example

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) => {
                IConfiguration configuration = hostContext.Configuration;

                WorkerOptions options = configuration.GetSection("WCF").Get<WorkerOptions>();

                services.AddSingleton(options);

                services.AddHostedService<Worker>();
            });
}

When the worker is being created it will be injected with its required dependencies.

Reference Configuration in ASP.NET Core

Lundt answered 1/10, 2019 at 11:54 Comment(8)
This answer is so perfect. It provides the correct way to read data from app-settings.Lathrop
You can use IConfiguration instead of creating a custom classApennines
Can you add an example of nested class configuration? How the appsettings.json will be? Example Database Interface Database Class{ String LocalIP { get; set; } MySql Databases Class { User Password DatabaseName ConnectionString } }Hiatt
@Hiatt it is not clear from your comment what you are asking. Can you post a detailed question that clarifies your issue?Lundt
@Lundt i mean if it's possible for you to add an example of nested class configuration file. This Example just suppose that you have two string, but what happen if i need a List<MyClass> Class that need to be populated from appsettings.json? How the appsettings.json need to be written and how my WorkerOption need to be declared?Hiatt
Can't we use IOptions instead of a singleton?Kowalewski
@Kowalewski yes you can if that is what you prefer.Lundt
thanks, valid also in .NET 6Matson
K
25

How I did it:

Progam.cs:

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) =>
            {
                IConfiguration configuration = hostContext.Configuration;
                services.Configure<RabbitMQConfiguration>(configuration.GetSection(nameof(RabbitMQConfiguration)));
                services.AddHostedService<Worker>();
            });
}

In your worker you can access the options like this:

public Worker(ILogger<Worker> logger, IOptions<RabbitMQConfiguration> options)
{
    _queue = options.Value.RabbitMQUrl;
    _options = options.Value;
    _logger = logger;
}

You'll also need a class for your options object:

public class RabbitMQConfiguration
{
    public string RabbitMQUrl { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
   // ...
}

In the appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "RabbitMQConfiguration": {
    "RabbitMQUrl": "rabbitmq://yoururl",
    "Username": "admin",
    "Password": "mypassword",
  }
}
Kowalewski answered 2/2, 2021 at 10:8 Comment(0)
G
1

in continuation of the answer Nikosi You can also set the options in the Startup class in the ConfigureService method, something like this:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }
    
    public void ConfigureServices(IServiceCollection services)
    {
        // others services configuration
        WorkerOptions options = 
            Configuration.GetSection("WCF").Get<WorkerOptions>();

        services.AddSingleton(options);
        services.AddHostedService<Worker>();
    }
}
Graecize answered 14/10, 2020 at 14:24 Comment(1)
Just as a side note here, the Startup.cs class is not available in a ServiceWorker project. The reason for this is that a ServiceWorker is based on IHostBuilder, and not IWebHostBuilder, which is what provides access to Startup.cs. There is a closed GitHubIssue on it here.Gripsack

© 2022 - 2024 — McMap. All rights reserved.