Learn how YOU can manage your app configuration in ASP .NET Core
Tags: dotnet, tutorial, csharp, webdev
Published: 2021-01-05 18:30:48

---
title: Learn how YOU can manage your app configuration in ASP .NET Core
published: true
description: Configuration is an important part of any app. You want to avoid recompiling your app and still make it flexible enough to run on dev, staging and prod. Learn how in ASP .NET
tags: dotnet, tutorial, csharp, webdev
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/pm5042dbwlkl6ikwpznr.jpg
---

TLDR; this article gives you a high-level view of configuration management for ASP .NET. It teaches you about various sources, providers and how to read config data. It also shows you how to make your config data type safe.

Why do we need configuration? Well, the answer is that we need our app to perform differently without necessarily recompile it. The reason for that is to make it flexible for different environments. It should be enough to just change a config value to get the app to go towards a completely new endpoint for example. ASP.NET has our back, it knows config data can come from a million different places, files, like JSON or ini, environment variables or even Azure. ASP .NET has a set of services, so called providers that reads out this config data for us.

What we need to know to work efficiently with it, is to know what providers exist (so we know where we can place our config data) and in what order the data is read, so we know what data will take effect. Also, it's helpful to know that all data are key-value pairs. It doesn't stop the data from being nested though. Don't worry there are two different ways we can deal with nesting, untyped and typed.

Let's just stress one more time some important dimensions on configuration management:

- **Config data can come from many sources**. Data can be read from a number of different sources like JSON files, environment variables, Azure Services and more. Here's a list of all the different sources:

  - Settings files, such as appsettings.json
  - Environment variables
  - Azure Key Vault
  - Azure App Configuration
  - Command-line arguments
  - Custom providers, installed or created
  - Directory files
  - In-memory .NET objects

- **Config data is read in a certain order**. Host configuration is the first thing to be read followed by JSON data found in _appsettings.json_ and an environment specific version. The complete read order looks like so:

  1. `ChainedConfigurationProvider` : Adds an existing `IConfiguration` as a source. In the default configuration case, adds the host configuration and setting it as the first source for the app configuration.

  1. **App setttings JSON file**. There's an _appsettings.json_ that is read using the JSON configuration provider.

  1. **Environment specific app settings JSON**. After the initial _appsettings.json_ is read it looks read from an environment specific app settings file. So essentially it's looking for a file on this format _appsettings.Environment.json_ using the JSON configuration provider. For example, _appsettings.Production.json_ or _appsettings.Development.json_.

  1. **App secrets**. If you are running in a dev environment it tries to read secrets next.

  1. **Environment variables**. After that it reads any environment variables  using the Environment Variables configuration provider.

  1. **Command-line arguments**. Lastly it reads command line arguments using the Command-line configuration provider.

  > NOTE, if the same key exist in two different sources it's the latter one that gets applied. For example i a key exist in _appsetttings.json_, that value will be overwritten if it also exist as an environment variable.

- **Don't place sensitive data as config data**. You need to know what data is sensitive or not. As a rule of thumb, place sensitive data in a service like Azure Key Vault, avoid placing such data in configuration files! You can still read from Azure Key Vault using a configuration manager, so in theory it's as straight forward as reading the data from a JSON file for example.

## References

I've left some hopefully useful links so you can learn more about all the different providers and options. Hopefully this article gives you a good foundation to continue learning.

- [Builder settings](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-5.0#default-builder-settings&WT.mc_id=dotnet-12379-chnoring)

- [Configuration management docs for ASP .NET](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0&WT.mc_id=dotnet-12379-chnoring)

## Read data

To read configuration data lets turn our attention to _Program.cs_ and it's constructor that should look like this:

```csharp
public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}
```

Here we are injecting an instance of `IConfiguration`. It will attempt to read data from _appsettings.json_ file, so how do we access it? Looking at the content of _appsettings.json_ we see that it looks like so:

```json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
```

To read a non-nested value like `AllowedHosts` we can type like so:

```csharp
Configuration["AllowedHosts"]  // *
```

However, if we need a nested value like Logging->LogLevel->Default we need to access it with `:` as separator. We instead type like so:

```csharp
var value = Configuration["Logging:LogLevel:Default"];
Console.WriteLine(value); // Information
```

> NOTE, you are likely to want to access this kind of data when setting up services so you are more likely to read from `Configuration` instance in `ConfigureServices()` method rather than the constructor, as is demonstrated above. With that said, all you need to do is to inject `IConfiguration` in any constructor where you might need it, like a controller for example, like so:

```csharp
public IndexModel(ILogger<IndexModel> logger, IConfiguration configuration )
{
    _logger = logger;
    _configuration = configuration;
    
    var serviceConfig = new ServiceConfiguration();
    _configuration.GetSection(serviceConfig.Services).Bind(serviceConfig);
    Console.WriteLine(serviceConfig.CartServiceUrl);
    Console.WriteLine(serviceConfig.ProductsServiceUrl);
}
```

### Demo, override a value

We stated earlier that the last provider to be read is the one that decided the value on the config data. Lets demo this by placing a setting `API` like so in _appsetting.json_:

```json
"API": "http://localhost:3000/api"
```

and now place a slightly different entry in _appsettings.Development.json_ like so:

```json
"API": "http://localhost:5000/api"
```

Change the code in the `Startup` constructor to read `API`, like so:

```csharp
var value = Configuration["API"];
Console.WriteLine(value); // http://localhost:5000/api
```

As you can see, the last read source, _appsettings.Development.json_ decided the value.

## Bind data, make it type safe

At some point, you might loose track of what configuration you have. You might have 10-20 different keys, or more, at various levels of nesting. The problem with that is that it's starting to get messy. Knowing what keys are actually used could be a combination of looking into JSON files, environment variables or even the source code. So how do you approach and manage such a mess?

The way to manage it is how you manage most things in .NET and C#, type it. The idea is to have variables or structures like classes for most things you plan to use, your config data shouldn't be any different. So how do we type our config data then - use a class.

Let's assume you have a few endpoints `ProductsServiceUrl` and `CartServiceUrl` that you want to keep track of and that they should be set to different values in different environment. You can create a class for that, like so:

```csharp
class ServiceConfiguration 
{
  public const string Services = "Services";
  public string ProductsServiceUrl { get; set; }
  public string CartServiceUrl { get; set; }
}
```

The next thing we want to do is to tell ASP .NET to _bind_ an instance of the above class to a specific section of our config. Imagine you know have a section in _appsettings.json_ that looks like this:

```json
"Services" : {
  "ProductsServiceUrl": "http://localhost:3000/products",
  "CartServiceUrl": "http://localhost:3000/cart" 
}
```

Once you've created a class for this and added the config data it's time to read out the data. We can read the data like by locating the constructor of `Startup` class in _Startup.cs_ and add the following code:

```csharp
var serviceConfig = new ServiceConfiguration();
Configuration.GetSection(serviceConfig.Services).Bind(serviceConfig);
Console.WriteLine(serviceConfig.CartServiceUrl); // http://localhost:3000/cart
Console.WriteLine(serviceConfig.ProductsServiceUrl); // http://localhost:3000/products
```

The use of `GetSection()` enables us to drill down into the part of the config that's interesting for us. Using `bind()` we _bind_ the sections content to an instance of `ServiceConfiguration` and thereby populate the instance with the values from the config section.

## Summary

You learned how config data can exist in various sources. You also learned how ASP .NET was able to read from these sources. Furthermore, you were shown various ways to read nested and un nested data. Lastly you looked at how to make your config data more type safe.