.Net Core 5 Console App, appsettings not loading for environment when done in Program.Main

Viewed 3052

I want my .Net Core 5 console application to select settings from the appropriate appsettings file based on the DOTNET_ENVIRONMENT environment variable. I'm testing this by running it in the Visual Studio 2019 debugger and fetching the environment from my launchSettings.json file.

In a .Net Core 5 console application I have 4 "appsettings" files:

  • appsettings.json
  • appsettings.Development.json
  • appsettings.Staging.json
  • appsettings.Production.json

Each file Properties is set to Build Action : Content, and Copy to Output Directory: Copy if newer.

In my launchSettings.json I have my environment set to "Staging" like so:

 {
  "profiles": {
    "MyAppName": {
      "commandName": "Project",
      "dotnetRunMessages": "true",
      "environmentVariables": {
        "DOTNET_ENVIRONMENT": "Staging"
      }
    }
  }
}

I need access to my configuration in the "Main" method in Program.cs, so in that class I am setting a module-level string variable "_environment" like so in the static constructor:

_environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");

This works; the value "Staging" gets loaded into the variable _environment.

I then load my Configuration into a static variable like so: (EDIT--this was my mistake, assuming this static property loaded AFTER the static ctor. In fact it loaded BEFORE the static ctor. This meant the _environment variable was not set, which means my environment-specific appsettings file never loaded).

private static IConfiguration Configuration { get; } = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
    .AddJsonFile($"appsettings.{_environment}.json", optional: true, reloadOnChange: true)
    .AddEnvironmentVariables()
    .Build();

When I then examine the contents of the Configuration, I see that it is only loading values from appsettings.json. It is not loading values from appsettings.Staging.json. The specific value I am looking for is "ConnectionStrings". This is how the ConnectionStrings section looks in appsettings.json:

"ConnectionStrings": {
    "ConnectionStringName": "Data Source=SqlDevelopment; Initial Catalog=MyTable; Integrated Security=SSPI;",
  }

And this is how that same section looks in appsettings.Staging.json:

"ConnectionStrings": {
    "ConnectionStringName": "Data Source=SqlStaging; Initial Catalog=MyTable; Integrated Security=SSPI;",
  }

But when I read the DataSource from the Configuration it is always "SqlDevelopment", even though the environment is Staging.

After trying and failing, I tried loading these 4 Nuget packages, but it had no effect:

  1. Microsoft.Extensions.Configuration
  2. Microsoft.Extensions.Configuration.Binder
  3. Microsoft.Extensions.Configuration.EnvironmentVariables
  4. Microsoft.Extensions.Configuration.Json

What am I missing?

3 Answers

Console applications check the DOTNET_ environment variables, not the ASPNETCORE_ variables. This isn't a new change. It goes back at least to .NET Core 3.1. You need to set DOTNET_ENVIRONMENT instead.

ASP.NET Core applications use any environment variables prefixed with ASPNETCORE_ in addition to the DOTNET_ variables.

From the docs :

The default configuration loads environment variables and command line arguments prefixed with DOTNET_ and ASPNETCORE_. The DOTNET_ and ASPNETCORE_ prefixes are used by ASP.NET Core for host and app configuration, but not for user configuration. For more information on host and app configuration, see .NET Generic Host.

PS: Just 10 minutes ago I had the same problem and realized I had set DOTNETCORE_ENVIRONMENT instead of DOTNET_ENVIRONMENT

I need access to my configuration in the "Main" method in Program.cs, so in that class I am setting a module-level string variable "_environment" like so in the static constructor:

_environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");

That's not how it works. You need to use IHostEnvironment.EnvironmentName. If you need access to your configuration in your Main() method, you're doing something wrong, and reading the environment variable is the wrong way to read the (ASP).NET Core Environment name.

The "(ASP).NET Core Environment name" can be set in multiple ways, one way being OS Environment variables. But they don't have to be, they can also be provided through command line arguments.

IHostEnvironment.EnvironmentName is the proper way to get the environment name.

Thank you everyone and especially @CodeCaster for helping me.

The issue is that the _environment variable was an empty string when the static Configuration was set. I assumed that since I was setting it in the static constructor it was available, but the static ctor was actually running after I set my static Configuration, so _environment was an empty string, so the "Staging" configuration was never loaded.

I altered the code so that there is no chance that the runtime will set variables in an order that I did not expect:

    private static IConfiguration Configuration { get; } = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")}.json", optional: true, reloadOnChange: true)
        .Build();

A number of posters essentially told me "you're doing it wrong." I realize that the runtime provides a dependency-injected Configuration after Host.CreateDefaultBuilder() is called. For reasons outside the scope of this question I happen to need Configuration before that call occurs, inside Program.Main. If I did not need Configuration in Program.Main, before the call to Host.CreateDefaultBuilder, none of this would be necessary.

Related