Import configuration correctly in our .NET applications

In this post, we are going to see how to correctly import this configuration in different environments, or even for our local development.

 

For this example, I am going to use the sample project I have on GitHub, which we have seen throughout this series.

 

 

 

1 - What is configuration in .NET?

When designing applications, we have elements that can vary depending on the environment in which we are deploying the application.

For example, when deploying the application to the testing server, we want to use the testing server's configuration, such as the database. However, if we deploy to production, we want everything to point to production.

We want this configuration to be different at runtime, which is why we must handle it this way and not by putting #IF Debug everywhere as it was done in the past.

 

 

1.1 - Default configuration in ASP.NET

When we create a new project in visual studio, such as an API project, it comes with an initial configuration or framework for the application, which provides the minimum requirements for the vast majority of applications.

For example, the dependency injection container so we can apply dependency injection, configuration for logging or the host in which it will run.

 

This configuration can be easily extended. Additionally, an options pattern for configuration is also specified in the framework. In the next post, we will see how to use it.

 

 

1.2 - Accessing configuration in .NET

The configuration comes in the Microsoft.Extensions.Configuration package, which is available in Nuget, but for ASP.NET Core applications, it is included by default.

Having the configuration separated in a package means we can create configurations even in projects that are not ASP.NET Core, such as console apps or tests.

 

1.3 - How configuration is defined in .NET

Configuration for an application is done as a set of Key-Value pairs, where the Value can also be another Key-Value pair.

 

The Key is used to identify which part of the configuration we are in, and so we can access it at runtime. The value contains the data the key refers to.

For example, conexionSQL = “Server=127.0.0.1;Port=3306;Database=webpersonal”.

  • Note: configuration files also support boolean and int types for the value.

 

1.3.1 - Hierarchical organization of configuration

As I mentioned before, a Key-Value can contain another key-value in the value. To access a key that is "inside" we use the following syntax ParentKey:ChildKey = "childValue", which allows us to have the configuration properly structured:

{
 "database": {
    "read": {
      "connectionString": "Server=127.0.0.1;Port=3306;Database=webpersonal"
    }
  }
}

In this case, to access the connection value, we will use the key database:read:connectionString.

 

 

2 - Defining configuration in .NET

To define this configuration in .NET, we use a .json file and in ASP.NET Core we do this in the appsettings.json file, which is included by default when creating the project. It contains the following configuration:

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

As you can see, it is a configuration related to logging and AllowedHost, which we will explain in another post.

 

What we want to do in this case is update the code so that it contains the database configuration instead of having it hardcoded in the ConfigureServices method, as we have right now.

public void ConfigureServices(IServiceCollection services)
{
    //other code
    services
        .AddScoped<DbConnection>(x => new MySqlConnection("Server=127.0.0.1;Port=3306;Database=webpersonal;Uid=webpersonaluser;password=webpersonalpass;Allow User Variables=True"));
}

 

The first step in our goal is very simple: move this configuration to the appsettings.json file, which will look like this:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "database": {
    "Server": "127.0.0.1",
    "Port": 3306,
    "DatabaseName": "webpersonal",
    "User": "webpersonaluser",
    "Password": "webpersonalpass",
    "AllowUserVairables": true
  },
  "AllowedHosts": "*"
}
  • Note: in an ideal scenario, this configuration should be stored in what are called Secrets, but for this example, it is sufficient. If you use secrets, you will need the URL that gives you access to the secrets in the configuration.

 

As a final point, IConfiguration provides an extension method called .GetConnectionString(name) by default, which will look for the connection you specify within ["ConnectionStrings:name"].

 

 

2.1 - Reading configuration in .NET with IConfiguration

Now what we have to do is read this configuration. For this, .NET provides us with the interface IConfiguration, which is included in the dependency injection container, so we can include it in the constructor of our startup.cs class.

public class Startup
{
    public IConfiguration Configuration { get; }

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }
    //Other methods
}

And once we have access to IConfiguration we access its values using the key. For example, to access the server of our database, we write IConfiguration["database:server"], and the same for the rest of the elements.

 

In my case, I created a class which will return the formatted URL

public static class Database
{
    public static string BuildConnectionString(IConfiguration config)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append($"Server={config[\"database:server\"]};");
        sb.Append($"Port={config[\"database:Port\"]};");
        sb.Append($"Database={config[\"database:DatabaseName\"]};");
        sb.Append($"Uid={config[\"database:User\"]};");
        sb.Append($"password={config[\"database:Password\"]};");

        if (config.GetValue<bool>("database:AllowUserVairables") == true)
        {
            sb.Append("Allow User Variables=True;");
        }

        return sb.ToString();
    }
}

 

As you can see, for the last parameter I am accessing it differently. This is because IConfiguration contains a method to automatically cast, but access is just as before, through the Key.

 

When a value is not found, the default value is used. When we do GetValue<T>, it uses the default value of T, and when we access by key directly, it will return null, the default value for string.

 

 

3 - Accessing sections in IConfiguration

The configuration in .NET allows us to use what is called a section, which lets us access different sections individually within the configuration. To do this, we just need to run the following code

IConfigurationSection section = config.GetSection("Database");

One of the main advantages is that when we specify a section, we don't have to write out the whole key again for that section.

And the code looks like the following:

public static string BuildConnectionString(IConfiguration config)
{
    IConfigurationSection section = config.GetSection("Database");
    
    StringBuilder sb = new StringBuilder();
    sb.Append($"Server={section.GetValue<string>(\"server\")};");
    sb.Append($"Port={section.GetValue<int>(\"Port\")};");
    sb.Append($"Database={section.GetValue<string>(\"DatabaseName\")};");
    sb.Append($"Uid={section.GetValue<string>(\"User\")};");
    sb.Append($"password={section.GetValue<string>(\"Password\")};");

    if (section.GetValue<bool>(\"AllowUserVairables\") == true)
    {
        sb.Append("Allow User Variables=True;");
    }

    return sb.ToString();
}

 

 

4 - Converting IConfiguration to an Object in .NET

Our journey with configuration does not end here, as we also have the possibility of converting a section of our configuration into a strongly typed object, in our case, a class, which allows us to reduce the number of hardcoded elements in the code.

 

The first thing we need is a class that is 1 to 1 with the configuration we want to use. For our database example, it will be the following:

public class DatabaseSettings
{
    public string Server { get; set;  }
    public int Port { get; set; }
    public string DatabaseName { get; set; }
    public string User { get; set; }
    public string Password { get; set; }
    public bool AllowUserVairables { get; set; }
}

 

Then we only need to use the method provided by IConfiguration called Bind and pass the key and an instance of the object to map:

public static string BuildConnectionString(IConfiguration config)
{
    DatabaseSettings dbSettings = new DatabaseSettings();
    config.Bind("database", dbSettings);
    StringBuilder sb = new StringBuilder();
    sb.Append($"Server={dbSettings.Server};");
    sb.Append($"Port={dbSettings.Port};");
    sb.Append($"Database={dbSettings.DatabaseName};");
    sb.Append($"Uid={dbSettings.User};");
    sb.Append($"password={dbSettings.Password};");

    if (dbSettings.AllowUserVairables == true)
    {
        sb.Append("Allow User Variables=True;");
    }

    return sb.ToString();
}

Now we can access the variables directly using the type, which is so much better.

 

 

5 - Overwriting configuration in .NET

When we develop code, it's not for it to run on our own machine, but rather to run on a server, commonly on several servers, and for each of them (dev, uat, production), the configuration parameters will be different.

 

In the example we have seen throughout the code, the database is located on localhost, but when we deploy to production it will be "EnProduccion" and the password will be changed to something more complex.

ASP.NET Core gives us the ability to define configuration that will be different per environment.

  • Note: environments in .NET are identified by the environment variable "ASPNETCORE_ENVIRONMENT", which can be set in both Visual Studio and Rider in the project properties.

 

The way we can create multiple configurations is by using .json files. We have already seen how to use appsettings.json for our configuration. What we can do is create another one for production, which must be in the same folder, and will be called appsettings.production.json:

appsettings.json example

And as we can see, Visual Studio places it "together" with the other appsettings files we have.

Now we simply need to overwrite the configuration that we want to change, in our case, the server and password:

{
  "database": {
    "Server": "EnProduccion",
    "Password": "C0nTr$Se!Dif1c1L"
  }
}

And in the code we don't have to change anything. If we deploy to production (ASPNETCORE_ENVIRONMENT = production), it will read the new values:

updated appsettings configuration

 

Conclusion

In this post, we saw what configuration is in .NET

How to access and set configuration in our .NET applications

Different ways to access configuration in .NET using IConfiguration

Convert configuration to an object

Overwrite configuration per environment in .NET

This post was translated from Spanish. You can see the original one here.
If there is any problem you can add a comment bellow or contact me in the website's contact form

Uso del bloqueador de anuncios adblock

Hola!

Primero de todo bienvenido a la web de NetMentor donde podrás aprender programación en C# y .NET desde un nivel de principiante hasta más avanzado.


Yo entiendo que utilices un bloqueador de anuncios como AdBlock, Ublock o el propio navegador Brave. Pero te tengo que pedir por favor que desactives el bloqueador para esta web.


Intento personalmente no poner mucha publicidad, la justa para pagar el servidor y por supuesto que no sea intrusiva; Si pese a ello piensas que es intrusiva siempre me puedes escribir por privado o por Twitter a @NetMentorTW.


Si ya lo has desactivado, por favor recarga la página.


Un saludo y muchas gracias por tu colaboración

© copyright 2025 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café