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"
}
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>
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
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>
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>
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"));
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
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
> dotnet run --configuration ReleaseLinux
this is a global setting
this is linux
overwritten by appsettings.user.json
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)