1

I have a .NET 6 console app that runs on multiple servers to monitor apps on them.

The multiple servers already have their environment variable setup for other apps that use these values.

For example:

Server A -> DOTNET_ENVIRONMENT = dev
Server B -> DOTNET_ENVIRONMENT = qa
Server C -> DOTNET_ENVIRONMENT = prod
Server D -> DOTNET_ENVIRONMENT = prod

Even though both servers C and D are prod servers, they need different appsettings because they monitor different apps in those servers. So, having one appsettings.prod.json to be used in servers C and D won't work in this case.

I don't want to mess with the value of DOTNET_ENVIRONMENT either because other apps running on that server are using it.

What I've done so far

I named appsettings.json files of my console app based on the server's name.

For example:

Added launchsettings profile to pass machine name as environment variable (for my testing):

{
  "profiles": {
    "AppStatusWatchDog (ServerA)": {
      "commandName": "Project",
      "environmentVariables": {
        "MachineName": "ServerA"
      },
      "dotnetRunMessages": "true"
    }
  }
}

And tried to load correct appsettings.json during startup:

public static void Main(string[] args)
{
    var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
    var pathToContentRoot = Path.GetDirectoryName(pathToExe);

    // Just calling Environment.MachineName is adequate here. GetEnvironmentVariable("MachineName") is here because we supply MachineName's value during local testing using launch profile.
    var machineName = Environment.GetEnvironmentVariable("MachineName") ?? Environment.MachineName;
    
    var config = new ConfigurationBuilder()
        .SetBasePath(pathToContentRoot)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{machineName}.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();

    // Rest of the code here...
}

Here config loads all the settings I have, i.e. appsettings.json, appsettings.ServerA.json and the environment variables which is good .

The issue comes up when I try to use config by injecting IConfiguration into a constructor of some class at which point, it loads appsettings.json and the environment variables but never the appsettings.ServerA.json.

What am I doing wrong here?

Ash K
  • 1,802
  • 17
  • 44
  • The only thing that comes to mind is that `pathToContentRoot` might not be correct when running in local debug or maybe the json file is not copied from project root to the build output directory... I'd check what `pathToContentRoot` is and verify that the files are present there. If they are - set `optional` to false and see what error you get. – fredrik Aug 15 '23 at 20:51
  • Do you set a breakepoint at `var machineName` ? Can you get the machinename ServerA? – Qing Guo Aug 16 '23 at 03:54
  • Figured this out and added my answer below. Thank you. – Ash K Aug 16 '23 at 14:04

2 Answers2

0

you should modify your launch settings as follows:

{
  "profiles": {
    "AppStatusWatchDog (ServerA)": {
      "commandName": "Project",
      "commandLineArgs": "--MachineName=ServerA",
      "dotnetRunMessages": "true"
    }
  }
}

Next, you need to modify your Main method to correctly parse the command line arguments:

public static void Main(string[] args)
{
    var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
    var pathToContentRoot = Path.GetDirectoryName(pathToExe);

    // Parse command line arguments to find the machine name
    var machineName = "default"; // Default value if no argument is provided
    for (int i = 0; i < args.Length; i++)
    {
        if (args[i] == "--MachineName" && i + 1 < args.Length)
        {
            machineName = args[i + 1];
            break;
        }
    }

    var config = new ConfigurationBuilder()
        .SetBasePath(pathToContentRoot)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{machineName}.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();

    // Rest of the code here...
}

let me know if it's resolutive for your problem.

  • 1
    Sorry, that didn't solve my problem. Thank you for your answer though. I just wrote my answer that worked. Check it out if you'd like. – Ash K Aug 16 '23 at 14:02
0

The appsettings.X.json config file is loaded into IConfiguration based on the value of DOTNET_ENVIRONMENT variable for console apps. (ASPNETCORE_ENVIRONMENT for web apps. More info here).

Try this:

public static void Main(string[] args)
{
    var machineName = Environment.GetEnvironmentVariable("MachineName") ?? Environment.MachineName;
    
    // IMPORTANT part: 
    Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", machineName); 
    // For machine level change (Don't do this here):
    // Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", machineName, EnvironmentVariableTarget.Machine); // Reference: https://stackoverflow.com/a/19705691/8644294

    // If you're going to use config through dependency injection, you don't even
    // need to build 'config' below and this line CAN BE REMOVED altogether.
    // (Example of using config using dependency injection is shown below).
    // But for whatever reason you need 'config' in your 'Rest of the code here...'
    // part, keep this as is:
    var config = new ConfigurationBuilder()
        //.SetBasePath(Directory.GetCurrentDirectory()) // Not really needed though. Uncomment it if you'd like.
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{machineName}.json", optional: false, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();

    // Rest of the code here...
}

Use it like this:

public class SomeClass
{
    private readonly IConfiguration _config;
    
    public SomeClass(IConfiguration config)
    {
        _config = config; //  This will have settings from appsettings.json, appsettings.ServerA.json, environment variables of the machine it's running on and so on.
    }
}

Note: Properties of your appsettings.json should look like this:

Ash K
  • 1,802
  • 17
  • 44