In the previous post we talked about Configuration Sources and Configuration Provider - two different concepts but intertwined together.
We also spoke in brief about how this
var builder = WebApplication.CreateBuilder(args);
wires up the defaults for our apps (in terms of logging, dependency injections, configuration, etc. )
We also established that appsettings.json
and appsettings.{environment}.json
are amongst the default configuration sources and are loaded by default through the json configuration provider. In addition to the defaults, we can also add our own custom sources and of course they would need their own custom configuration providers defined (i.e. you need to write your own).
Let's look at how you can access the configuration within your application from the builder
Say you have an appsettings.json file
{
"ConnectionStrings": {
"SqliteConnection": "Filename=:memory",
"PostgresConnection": "Post gres connection string goes here"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Message": "Roofus Doofus !!"
"Title" : {
"HomePage" : "Stockholm i mitt hjärta"
}
}
And you want to access the Message and the ConnectionStrings in your application. Since appsettings.json is shipped by the .NET framework and loaded by default, we don't have to worry about that part.
We can access the Message config from the file ,in its most basic form through an indexer on the Configuration
collection provided by the builder
object.
var message = builder.Configuration["Message"];
The config which we want to access becomes the key here
The connection string for Sqlite can be retrieved as :
var sqlConnectionString = builder.Configuration.GetConnectionString("SqliteConnection");
If you closely observe SqliteConnection is a level below (or nested within) ConnectionStrings . Usually, if we have to access a nested config then we do it through the ":" notation.
But the GetConnectionString is an extension method provided by the framework and it does the following behind the scenes
public static string GetConnectionString(this IConfiguration configuration, string name)
{
return configuration?.GetSection("ConnectionStrings")?[name];
}
But since the DI is one of the key highlights of .NET framework, we can inject the configuration as a dependency which will be resolved by .NET framework itself
The Configuration
on the builder
is of type ConfigurationManager
which is a concrete implementation of IConfigurationBuilder
, IConfigurationRoot
IConfigurationRoot
extends IConfiguration
and as msdn highlights here , IConfiguration
represents a set of key/value application configuration properties and that is what we are interested in.
So we inject a dependency of IConfiguration
in our services if we want to access any configuration within it
Following is a snippet from a RouteEndpoint
(an endpoint handler) from the minimal api scaffold
app.MapGet("/", ([FromServices] IConfiguration configuration) =>
{
var sb = new StringBuilder();
//By default the appsettings.json is loaded by the json configuration provider when the host is built
//if we have to access the configuration, we can do it via the IConfiguration interface
//The hierarchical structure in the json file is flattened like this. you access the children through ":" notation
var title= configuration.GetValue<string>("Title:HomePage");
sb.Append($"Homepage Title : {title} \n");
//If you try to access a non-existent key, it will set the default value
var nonExistentKeyInConfiguration = configuration.GetValue<bool>("Title:Shopping");
sb.Append($"Trying to access a non-existent key from the configuration will set the default value of T on GetValue<T>: {nonExistentKeyInConfiguration} \n");
//And if you try to cast the config to an invalid cast, e.g. our Title config is given as a string and we try to cast it to int then we will get an exception
try
{
var inconsistentTypeCasting = configuration.GetValue<int>("Title:HomePage");
}
catch (Exception e)
{
sb.Append($"Inconsistent type casting of a config value while retrieving it will throw an exception of {e.InnerException?.Source}\n");
}
return sb.ToString();
});
Lets look at a more complex config and how to map it to a csharp complex type
You have the following in your appsettings.json
"Pet": {
"Name": "Roofus",
"Description": "I climb through people's house fences and scare them'",
"IsFriendly": false,
"Age": 5
}
For this you would create a complex type say Pet :
(OBS - The type name can be different than the config name, it does not have to match)
public class Pet
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public bool IsFriendly { get; set; }
public int Age { get; set; }
}
Going back to our json config,
"Pet": {
"Name": "Roofus",
"Description": "I climb through people's house fences and scare them'",
"IsFriendly": false,
"Age": 5
}
"Pet" is referred as a section and we bind the "section" calling the Bind on the IConfiguration
injected in our service
//we create the pet object where we want to bind the configuration
var petUsingBind = new Pet();
//Call the bind with the section name and pass the object created.
configuration.Bind("Pet", petUsingBind);
To wrap up ,
In the previous post we saw that the default configurations are loaded in a particular order and the order in which the configuration sources are loaded determines if a config overrides the other i.e. the order matters !
Well, the good news is we can override the default behaviour and define our own through the ConfigureAppConfiguration
available on the host
exposed through builder
builder.Host.ConfigureAppConfiguration((ctx, configBuilder) =>
{
//we clear all the config sources previously read in the default order
configBuilder.Sources.Clear();
var env = ctx.HostingEnvironment;
configBuilder.AddEnvironmentVariables("ASPNETCORE_");
//we are only reading the appsettings.json file here. explicitly skipping the environment specific
configBuilder.AddJsonFile("appsettings.json",optional:true,reloadOnChange:true);
//We skip the user secrets
//We skip the command line arguments as well
});
We will look at the options pattern in the next post
References
The msdn documentation on fundamentals of configuration is really good and has all you need to know information. Please check it out to explore more in this area
Customization through ConfigureAppConfiguration
Further reading on ConfigurationManager (type of builder.Configuration)
Top comments (0)