DEV Community

Vidisha Parab
Vidisha Parab

Posted on

Basics of accessing the Configuration in .NET Web App

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"
       }
}
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

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];
        }
Enter fullscreen mode Exit fullscreen mode

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();
});
Enter fullscreen mode Exit fullscreen mode

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
    }
Enter fullscreen mode Exit fullscreen mode

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; }
 }
Enter fullscreen mode Exit fullscreen mode

Going back to our json config,

    "Pet": {
        "Name": "Roofus",
        "Description": "I climb through people's house fences and scare them'",
        "IsFriendly": false,
        "Age": 5
    }
Enter fullscreen mode Exit fullscreen mode

"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);

Enter fullscreen mode Exit fullscreen mode

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 !

Config Sources in Order

Well, the good news is we can override the default behaviour and define our own through the ConfigureAppConfigurationavailable on the hostexposed 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

});

Enter fullscreen mode Exit fullscreen mode

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)