The ASP.NET Core Configuration system provides an excellent mechanism for storing & reading the configuration data from various sources like JSON files, INI Files, XML files, environment variables, command-line arguments, etc. ASP.NET Core uses the Configuration Providers to read from the Configuration Sources. We can extend it by creating our own Custom Configuration Provider and read from a Custom Configuration Source. This tutorial covers the basics of the configuration system in ASP.NET Core.
Table of Contents
What is Configuration
The Configuration is the parameters or the initial settings specific to the application. These settings are stored separately from the code and in an external file. This gives the developers and administrators control and flexibility over the way applications are run.
For example, the Connection String
required to connect to the database is stored in a configuration file. By Modifying the connection string you can change the database name, location, etc without modifying the code.
The ASP.NET Applications used the Web.config
to store the Configurations. For Example, the connection string is stored as shown below
1 2 3 4 5 6 | <connectionStrings> <add connectionString="data source=ABC;Integrated Security=SSPI; initial catalog=xyz" providerName="System.Data.SqlClient"/> </connectionStrings> |
Configuration Sources
The ASP.NET Core allows us to read the configuration values from the various sources and file formats. Some of the commonly used sources are
- Json files
- INI files
- XML files
- Command-line arguments
- Environment variables
- Azure Key Vault
- Azure App Configuration
- Directory files
- Encrypted User Secrets
- In-memory .NET objects
- Custom providers
All of them store the configuration values in different formats. The configuration system reads them and combines them into a single unified configuration model.
Configuration Provider
The ASP.NET Core uses the Configuration Providers to read the configuration. Each configuration source has its own Provider. The following is the list of providers available to ASP.NET Core apps.
Configuration Provider | Configuration Source | Included in Default Configuration |
---|---|---|
Azure Key Vault configuration provider | Azure Key Vault | No |
Azure App configuration provider | Azure App Configuration | No |
Command-line configuration provider | Command-line parameters | Yes |
Environment Variables configuration provider | Environment variables | Yes |
INI configuration provider | INI Files | No |
JSON configuration provider | JSON File | Yes |
XML configuration provider | XML File | No |
Key-per-file configuration provider | Directory files | No |
Memory configuration provider | In-memory collections | No |
Secret Manager | File in the user profile directory | No |
How Configuration is stored
We specify the configurations key-value pairs, but how we store them depends on the Configuration Source.
Following is an example configuration in JSON format. You can see that we can arrange them in a multi-level hierarchical format.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | { "option1": "value1", "option2": "2", "mainOption": { "suboption1": "subvalue1", "suboption2": "subvalue2" }, "Loging" : { "LogLevel" : { "Default" : "", "Microsoft": "Warning" } } } |
Similarly, an INI configuration file is as shown below. You can use the [mainOption]
to group settings. Also, use the :
as in [Logging:LogLevel]
to create a multi-level hierarchy.
1 2 3 4 5 6 7 8 9 10 11 12 | option1="Value1" option2="2" [mainOption] suboption1="subvalue1" suboption2="subvalue2" [Logging:LogLevel] Default=Information Microsoft=Warning |
Configuration is flattened to Key/Value Pair
It does not matter how & where the configuration is stored, once the configuration system loads it flattens them to key/value pair
For Example, the following hierarchical json configuration
1 2 3 4 5 6 7 8 9 10 | { "Loging" : { "LogLevel" : { "Default" : "Information", "Microsoft": "Warning" } } } |
is flattened to the following key/value pair.
1 2 3 4 | Loging:LogLevel:Default="Information" Loging:LogLevel:Microsoft="Warning" |
The important points are
- Keys are case-insensitive. For example,
ConnectionString
andconnectionstring
are treated as equivalent keys. - Values are strings. (The booleans are represented as string “True” or “False”)
- The hierarchy delimiter is a colon (
:
). - You can also use double underscore (
__
), which automatically converted into a colon (:)
. - In Azure Key Vault, hierarchical keys use
--
as a separator. Configuration system automatically replaces it with:
How to load the Configuration
If we create the new ASP.NET Core application using the dot new
command or using the Visual Studio, then code for loading the configuration is already set up by default for us.
Default configuration
Open Visual Studio 2019 and create a new ASP.NET Core 3.1 Empty project. If you are new to ASP.Net core, we recommend you to read the tutorial
Open program.cs
It has the Main method, which is the entry point to the application.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } |
The Main
method calls the CreateDefaultBuilder
, which is a helper class. You can take a look at the source code from the GitHub.
One of the tasks performed by the CreateDefaultBuilder
is to load the configuration from various sources. The following is the relevant part of the source code from CreateDefaultBuilder
method.
The part of the code where configurations are loaded is in builder.ConfigureAppConfiguration
method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | public static IWebHostBuilder CreateDefaultBuilder(string[] args) { var builder = new WebHostBuilder(); if (string.IsNullOrEmpty(builder.GetSetting(WebHostDefaults.ContentRootKey))) { builder.UseContentRoot(Directory.GetCurrentDirectory()); } if (args != null) { builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build()); } builder.ConfigureAppConfiguration((hostingContext, config) => { var env = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); if (env.IsDevelopment()) { var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); if (appAssembly != null) { config.AddUserSecrets(appAssembly, optional: true); } } config.AddEnvironmentVariables(); if (args != null) { config.AddCommandLine(args); } }) ... // Code for Configure Logging ... return builder; } |
ConfigureAppConfiguration
The ConfigureAppConfiguration
method takes two parameters. HostingContext
& config
.
HostingContext
: is an instance of HostBuilderContext
. It exposes the property HostingEnvironment
, which lets us know whether we are running Development (isDevelopment
), Production (IsProduction
) or staging (IsStaging
) environment
config
: which is an instance of IConfigurationBuilder
. it exposes several helper methods, which we can use to load the configuration file
The default Configuration loaded loads the configuration from the following sources
appsettings.json
appsettings.Environment.json
For example,appsettings.Production.json
andappsettings.Development.json
.- App secrets when the app runs in the
Development
environment. - Environment variables.
- Command-line arguments.
appsettings.json
The AddJsonFile
Loads the configuration from the json file.
1 2 3 4 5 | config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); |
The first argument is the name of the json file. The path is relative to the ContentRoot
.
If the second parameter is optional
. if it is set to true the AddJsonfile
does not throw any errors if the appsettings.json
file is not found.
The third parameter reloadOnChange
if true, reloads the configuration if contents of the configuration file changes. There is no need to restart the application.
The next line of code loads the which matches the name appsettings.{env.EnvironmentName}.json
This allows us to have different configuration files for each environment like appsetting.development.json
, etc
The ASP.NET Core determines the current environment for the ASPNETCORE_ENVIRONMENT
variable.
You can read more about appsettings.json.
User secrets
This is a new feature in ASP.NET Core, where the configuration is stored outside the repository. It’s called user secrets
and applicable only in the Development environment.
env.IsDevelopment()
is true only if the ASPNETCORE_ENVIRONMENT
is set to Development
1 2 3 4 5 6 7 8 9 10 | if (env.IsDevelopment()) { var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); if (appAssembly != null) { config.AddUserSecrets(appAssembly, optional: true); } } |
Environment variables
The AddEnvironmentVariables
reads all the user and system environment variables.
1 2 3 | config.AddEnvironmentVariables(); |
Command line arguments
The AddCommandLine
loads all the command line arguments to the configuration
1 2 3 4 5 6 | if (args != null) { config.AddCommandLine(args); } |
The configuration systems load the command line arguments twice.
The first time it reads to configure the host itself. The configuration related to the host itself is stored in the hosting environment variables that start with ASPNETCORE_
. (For Example, ASPNETCORE_ENVIRONMENT
, which ASP.NET Core uses to load the environment-specific configuration like appsettngs.development.json
etc.)
The command-line arguments are loaded again because it has higher precedence over appsettings.json, users’ secrets & environment variable. It is is ensure that settings of command-line arguments are not overwritten.
Custom Configuration
The built-in default configuration is sufficient for most of the applications. We can also easily extend it to suit our requirements.
The default configuration loads from the appsettings.json, command-line arguments & environment variables.
We can read the configurations from custom configuration files like custom.json
or custom.ini
as shown in the example below.
Create a custom.json
& custom.ini
. in the application root folder.
custom.json
1 2 3 4 5 6 7 8 9 10 11 12 | { "AllowedHosts": "*", "CustomSetting": { "CustomLevel": { "Option1": "Custom Value" } } } |
custom.ini
1 2 3 4 5 6 7 | option="Test" [mainOption] suboption1="subvalue1" suboption2="subvalue2" |
Right-click on both the files select Properties and Select Copy to output directory
to Copy always
.
Now, open the program.cs
and the update the code as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((ctx, bld) => { bld.AddJsonFile("custom.json"); bld.AddIniFile("custom.ini"); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); |
The code uses the ConfigureAppConfiguration
method to load the configurations from the custom.json
& custom.ini
files
Reading the Configuration
Finally it is time to read the configurations in our code.
The ASP.NET core configuration systems load all the configurations from various sources and make all of them available to use in the Configuration Service.
We use the dependency injection to get the instance of configuration service (using the interface IConfiguration
) in the constructor of our startup class. Note that configuration service is one of the few classes, which is available to be injected in the startup class. The startup constructor is as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | using Microsoft.Extensions.Configuration; public class Startup { public IConfiguration Configuration { get; } public Startup(IConfiguration configuration) { Configuration = configuration; } ... ... |
Similarly, you can inject You can use the same technique in the controller class.
To Read the values use the GetSection
method of the configuration object.
1 2 3 | await context.Response.WriteAsync("<div>" + Configuration.GetSection("message").Value + "</div>"); |
In case of multi-level hierarchical configuration we can use the :
to separate each level as show below
1 2 3 4 5 6 7 8 9 10 | { "Loging" : { "LogLevel" : { "Default" : "", "Microsoft": "Warning" } } } |
1 2 3 | Configuration.GetSection("Loging:LogLevel:Microsoft").Value |
Summary
We looked at how the new Configuration system works in ASP.NET Core. The ASP.NET Core Empty project template sets up the application to read the configuration from appsettings.json
, appsettings.{env.EnvironmentName}.json
, user secrets, environment variables, and command-line arguments. We can extend this by adding our own configuration files.