DEV Community

Rogelio Gámez
Rogelio Gámez

Posted on

Using multiple appsettings.json to release to different platforms in .NET

Challenges

You need to release the same application to different platforms with different settings, for example, different log configurations. And you also want to ease up the release process instead of configuring by managing that complexity from the app itself.

There is also a request from the user to be able to overwrite these settings, but you don't want them messing up the big appsettings.json you have set up. You only want them to focus on the two or three properties that they are interested in.

Solutions

You can load different settings files depending on the build configuration using preprocessor directives.

Taking advantage of the IConfiguration file load order, we can add support for a user settings file that will only contain specific properties.

Settings files

You have an application with five different settings files:

  • appsettings.json: contains the global settings for the application to work correctly.
  • appsettings.debug.json: in this file, we have the correct settings to run the application in debug mode on your dev environment.
  • appsettings.azure.json: we override properties for the app to work on Azure.
  • appsettings.linux.json: we override properties for the app to work on Linux.
  • appsettings.user.json: in this file, we only have the properties that the user may want to override when they deploy the application.

appsettings.json has the following properties:

{
  "GlobalSetting": "this is a global setting",
  "PlatformSetting": "default for platform setting",
  "UserSetting": "default for user setting"
}
Enter fullscreen mode Exit fullscreen mode

The properties mean:

  • GlobalSetting: this setting is shared between all the possible deployment platforms.
  • PlatformSetting: this setting is platform specific, it needs to be different in Linux, Azure, and debug.
  • UserSetting: this setting will be overridden by the user if they need it.

The debug, Linux, and Azure files will only configure the PlatformSetting and the user file will only configure the UserSetting.

Why do we have a specific appsettings.user.json?

Sometimes, the user needs to override certain settings from your application, but you have a very complex appsettings.json. Using a specific file for the user solves the following problems:

  • The user can only on the properties they care about, they don't need to handle your massive global settings file.
  • We have the option to not include the user file in our project. If we chose to not include it, we can safely update our application without overwriting the user settings file in the process.

Project build configurations

To support different platforms, we need to set up our project with different configurations:

For this example, our .csproj file has the three different configurations we support:

<PropertyGroup>
    ...
    <Configurations>Debug;ReleaseAzure;ReleaseLinux</Configurations>
</PropertyGroup>
Enter fullscreen mode Exit fullscreen mode

This is how the .sln is configured:

GlobalSection(SolutionConfigurationPlatforms) = preSolution
    Debug|Any CPU = Debug|Any CPU
    ReleaseAzure|Any CPU = ReleaseAzure|Any CPU
    ReleaseLinux|Any CPU = ReleaseLinux|Any CPU
EndGlobalSection
Enter fullscreen mode Exit fullscreen mode

Defining constants

Next, we need to define some constants we can use in the preprocessor directives.

If you want to know more about preprocessor directives, please visit https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives.

We can define them in the .csproj:

<PropertyGroup Condition=" '$(Configuration)' == 'ReleaseAzure' ">
    <DefineConstants>AZURE</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'ReleaseLinux' ">
    <DefineConstants>LINUX</DefineConstants>
</PropertyGroup>
Enter fullscreen mode Exit fullscreen mode

Loading settings files

First, we need to include these three NuGet packages in our project:

<ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.3" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
</ItemGroup>
Enter fullscreen mode Exit fullscreen mode

Next, we will be loading the settings files using IConfiguration.AddJsonFile, and use the #if preprocessor directive to select which platform file to load.

This example uses a console application in dotnet 6. Program.cs looks like this:

using Microsoft.Extensions.Configuration;

var configurationBuilder = new ConfigurationBuilder();

configurationBuilder.AddJsonFile("appsettings.json");

#if DEBUG
configurationBuilder.AddJsonFile("appsettings.debug.json");
#elif AZURE
configurationBuilder.AddJsonFile("appsettings.azure.json");
#elif LINUX
configurationBuilder.AddJsonFile("appsettings.linux.json");
#endif

configurationBuilder.AddJsonFile("appsettings.user.json");

var configuration = configurationBuilder.Build();

Console.WriteLine(configuration.GetValue<string>("GlobalSetting"));
Console.WriteLine(configuration.GetValue<string>("PlatformSetting"));
Console.WriteLine(configuration.GetValue<string>("UserSetting"));
Enter fullscreen mode Exit fullscreen mode

Testing the configurations

Now, if we run our application using the debug configuration, it will take the configuration of the debug settings file:

> dotnet run --configuration Debug
this is a global setting
this is debug
overwritten by appsettings.user.json
Enter fullscreen mode Exit fullscreen mode

We can also do the same for other configurations:

> dotnet run --configuration ReleaseAzure
this is a global setting
this is azure
overwritten by appsettings.user.json
Enter fullscreen mode Exit fullscreen mode
> dotnet run --configuration ReleaseLinux
this is a global setting
this is linux
overwritten by appsettings.user.json
Enter fullscreen mode Exit fullscreen mode

Final thoughts

This was a very simple tutorial, but I hope you can see the advantages of this approach.

For example, in your build pipeline, you can publish for different configurations by simply changing the configuration in the command. The application already handles the complexity of different platforms within itself.

Another advantage is that you can have as many "users" settings files as you want. Your team can handle the global and platform settings files, and different teams in your org can handle their files with the properties they care about. Just don't forget to have all of them properly documented in your org knowledge base to avoid any confusion!

The code used for this post can be found at: https://github.com/rogeliogamez92/blog-multiple-apsettings.

Top comments (0)