Loading Configuration in .NET 6 Worker Service

I was updating the Falcon Pi Twitter project from .NET 5 to .NET 6. As part of this upgrade, I created new projects so that I could use the templates that were designed for .NET 6.

Yes I could have used the .NET 5 template that came with Program.cs and Startup.cs, but I wanted to expand my knowledge of how to do things with only the Program.cs file that is provided as part of .NET 6 templates. This application is built using the Worker Service template.

With .NET 5, the configuration was part of the Startup class. Since there is no longer a Startup class, I did not know where it should be placed. I did the research on Stack Overflow but did not come up with any answers.

After some time, I did figure out what needed to be done and wrote this post to share that same information and some of the challenges that I faced when setting things up.

Getting the appsettings.json Loaded

With my projects, I usually have two appsettings.json files. One for production and one for development configuration. Production file is called appsettings.json and the Development file is called appsettings.Development.json.

I found that you have to use the Configuration Builder to load the appsettings.json file. You do this by adding the following to the Program.cs file.

IConfiguration configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", true, true)
    .Build();

You can add multiple JSON files to the configuration. For example, if you have a configuration file that is provided or used by another applicatoin, but also needs to be accessed by
the application that you are building, you can add another AddJsonFile line in the file for additional configurations.

Since I wanted to able to source appsettings.json for each environment, I updated the AddJsonFile to

.AddJsonFile("appsettings.Development.json", true, true)
.AddJsonFile("appsettings.json", true, true)

The problem that I then found out, is that when you do this, the second configuration file listed will overwrite the values in the first configuration value listed. Thus if you are working in Development, you will be running with the Production configuration values, and that would not be the correct way of doing things.

I then added a configuration switcher based on the environment variable that is defined by the application. The code looks as follows:

string appSettingsFile = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") == "Production" ?
     "appsettings.json" : "appsettings.Development.json";

IConfiguration configuration = new ConfigurationBuilder()
    .AddJsonFile(appSettingsFile, false, true)
    .Build();

To explain the lines above, the application is looking at the DOTNET_ENVIRONMENT variable, formerly ASPNETCORE_ENVIRONMENT variable in previous .NET versions, to determine which appsettings file to use. From that, the appsettings file is used in the configuration.

Note that the second parameter in the AddJsonFile is now set to false because we want the application to throw an error if the correct appsettings.json file does not exist.

Use AppSettings with Dependency Injection

Now that we have the configuration loaded and in the Program.cs file, we can then use it via Dependency Injection (DI) in our application.

Inside of the ConfigureServices extension method of the IHost, add the following

AppSettings appSettings = configuration.GetSection(nameof(AppSettings)).Get<AppSettings>();
services.AddSingleton(appSettings);

Final Result

After doing the above, you should have a Program.cs file that looks similar to the below.

string appSettingsFile = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") == "Production" ?
     "appsettings.json" : "appsettings.Development.json";

IConfiguration configuration = new ConfigurationBuilder()
    .AddJsonFile(appSettingsFile, false, true)
    .Build();

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        AppSettings appSettings = configuration.GetSection(nameof(AppSettings)).Get<AppSettings>();
        services.AddSingleton(appSettings);
    })
    .Build();

await host.RunAsync();

Then you can use the configuration when starting other services within the ConfigureServices method and by referencing the appSettings variable or within your other classes by using Dependency Injection and passing in AppSettings appSettings as part of the class constructor.

Posted: 2022-06-12
Author: Kenny Robinson, @almostengr