La .NET CONF 2024 acaba de terminar. Descubre las NOVEDADES de .NET 9 junto a los CAMBIOS que trae C# 13.
Puedes ver el contenido de este vídeo junto con su curso en el modo vídeo (similar a Udemy) si pulsas aquí.

Importar configuración correctamente a nuestras aplicaciones .NET

En este post vamos a ver cómo importar esta configuración de forma correcta en los diferentes entornos, o incluso para nuestro desarrollo local. 

 

Para este ejemplo voy a utilizar el proyecto de ejemplo que tengo en GitHub, que hemos visto a lo largo de esta serie.

 

 

 

1 - Qué es la configuración en .NET? 

Cuando diseñamos aplicaciones tenemos elementos que pueden variar dependiendo en qué entorno estamos desplegando la aplicación. 

Por ejemplo, cuando desplegamos la aplicación en el servidor de pruebas queremos utilizar la configuración del servidor de pruebas, como puede ser la base de datos. En cambio, si desplegamos en producción, queremos que todo esté apuntando a producción

Esta configuración deseamos que sea diferente en tiempo de ejecución por eso debemos hacerlo así y no poniendo #IF Debug por todas partes como se hacía en antaño. 

 

 

1.1 - Configuración por defecto en ASP.NET

Cuando creamos un nuevo proyecto en visual studio como puede ser un proyecto API este nos viene con una configuración o framework inicial de la aplicación, la cual van a ser los requerimientos mínimos para la gran mayoría de aplicaciones. 

Por ejemplo, el contenedor de dependencias para poder aplicar inyección de dependencias, Configraución para el logging o el host en el que va a correr. 

 

Esta configuración se puede extender muy fácilmente, Adicionalmente un patrón para la configuración, denominado “options pattern” tambien viene especificado en el framework, En el siguiente post veremos cómo utilizarlo.

 

 

1.2 - Acceder a la configuración en .NET

La configuración viene en el paquete Microsoft.Extensions.Configuration el cual está disponible en Nuget, pero para las aplicaciones ASP.NET Core, viene especificado por defecto.

Tener la configuración separada en un paquete significa que podemos crear configuraciones incluso en proyectos que no son ASP.NET Core, como por ejemplo en aplicaciones de consola, o tests. 

 

1.3 - Cómo se define la configuración en .NET 

La configuración de una aplicación se hace un un conjunto de Key-Value donde el Value puede ser otro Key-Value.

 

La Key se utiliza para identificar en qué parte de la configuración estamos, y así poder acceder en tiempo de ejecución. y el valor contiene los datos a los que hace referencia dicha key

Por ejemplo conexionSQL = “Server=127.0.0.1;Port=3306;Database=webpersonal”.

  • Nota: los ficheros de configuración también soportan tipos boolean e int para el valor. 

 

1.3.1 - Organización jerárquica de la configuración

Como he indicado en el punto anterior, un Key-Value puede contener en el value otro key-value para acceder a la key que está “dentro” lo hacemos a través de la siguiente sintaxis KeyPadre:KeyHija = “valorHijo”, lo que nos permite tener la configuración estructurada correctamente:

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

En este caso para acceder al valor de la conexión utilizaremos la siguiente key  database:read:connectionString.

 

 

2 - Definir la configuración en .NET

Para definir esta configuración en .NET lo hacemos a través de un fichero .json y en ASP.NET Core lo hacemos en el fichero appsettings.json, que nos viene por defecto al crear el proyecto. El cual contiene la siguiente configuración: 

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

Como vemos es una configuración relacionada con el logging y AllowedHost el cual veremos lo que es en otro post. 

 

Lo que queremos hacer en este caso es, actualizar el código para que contenga la configuración de la base de datos en vez de tenerla hardcoded en el método ConfigureServices como la tenemos ahora mismo.

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

 

El primer paso de nuestro objetivo es muy sencillo, mover esta configuración al fichero appsettings.json el cual va a quedar así: 

{
  "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": "*"
}
  • Nota: en un escenario ideal esta configuración tendría que estar en lo que se denominan Secrets, pero para el ejemplo nos sirve, en el caso de utilizar secrets, en la configuración necesitarás la url que te de acceso a los secrets

 

Como punto final, IConfiguration provee un extension method llamado .GetConnectionString(nombre) por defecto, que buscará la conexión que le indiques dentro de [“ConnectionStrings:nombre”].

 

 

2.1 - Leer configuración en .NET con IConfiguration

Ahora lo que debemos hacer es leer esta configuración. Para ello .NET nos provee de la interfaz IConfiguration, la cual esta includia en el contenedor de dependencias, por lo que podemos incluirla en el constructor de nuestra clase startup.cs

public class Startup
{
    public IConfiguration Configuration { get; }

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }
    //Otros métodos
}

y una vez tenemos acceso a IConfiguration accedmos a sus valores a través de la key, por ejemplo, para acceder al server de nuestra base de datos escribiremos IConfiguration[“database:server”] y lo mismo para el resto de elementos. 

 

En mi caso, he creado una clase la cual va a devolverme la url formateada

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();
    }
}

 

Como puedes observar para el último parámetro estoy accediendo de forma diferente. Esto es porque IConfiguration contiene un método para hacer casting automáticamente, pero el acceso es igual que antes, a través de la Key.

 

Cuando un valor no es encontrado, se utiliza el valor por defecto, cuando hacemos GetValue<T> utiliza el valor por defecto de T, y cuando accedemos por key directamente nos devolverá null, el valor por defecto de string.

 

 

3 - Acceder a secciones en IConfiguration

La configuración en .NET nos permite utilizar lo que se denomina section lo cual nos permite acceder a diferentes secciones de forma individual dentro de la configuración. Para ello únicamente debemos de ejecutar el siguiente código

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

Una de las grandes ventajas es que cuando indicamos una sección, no tenemos que volver a escribir toda la parte de la key que está en esa sección.

Y el código se ve de la siguiente manera:

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 - Convertir IConfiguration en un Objeto en .NET

Nuestra andadura con la configuración no acaba aquí, sino que tambien disponemos de la posibilidad de convertir una sección de nuestra configuración en un objeto tipado, en nuestro caso, una clase, lo cual nos permite reducir el numero de elementos hardcoded en el código.

 

Lo primero que necesitamos es una clase que sea 1 a 1 con la configuración que queremos utilziar, para nuestro ejemplo de la base de datos será la siguiente:

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; }
}

 

Y posteriormente únicamente debemos utilizar el método que nos provee IConfiguration llamado Bind y le pasamos la key y una instancia del objeto  a mapear:

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();
}

Ahora podemos acceder a las variables utilizando directamente el tipo, lo cual es mucho mejor.

 

 

5 - Sobreescribir configuración en .NET 

Cuando desarrollamos código, no lo hacemos para que se ejecute en nuestro ordenador, sino que lo hacemos para que se ejecute en un servidor, comúnmente lo hará en varios, y para cada uno de ellos (dev, uat, producción) los parámetros de la configuración serán diferentes. 

 

En el ejemplo que hemos visto durante todo el código, la base de datos está ubicada en localhost, pero cuando despleguemos a producción será "EnProduccion" así como la contraseña cambiará a una más compleja.

ASP.NET Core nos da la posibilidad de definir configuración que va a ser diferente por entorno

  • Nota: los entornos en.NET se identifican con la variable de entorno "ASPNETCORE_ENVIRONMENT", la cual se puede definir tanto en visual studio como en rider en las propiedades del proyecto. 

 

La forma en la que podemos crear múltiples configuraciones es utilizando los ficheros .json, hemos visto como  utilizar appsettings.json para nuestra configuración, lo que podemos hacer es crear otro para producción, el cual, tiene que estar ubicado en la misma carpeta, y se llamará appsettings.production.json :

appsettings.json example

Y como vemos visual studio nos lo ubica “junto” a los otros ficheros appsettings que tengamos.

Ahora únicamente debemos sobreescribir la configuración que deseamos modificar, en nuestro caso, el servidor y la contraseña:

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

Y en el código no tenemos que tocar nada, si desplegamos a producción (ASPNETCORE_ENVIRONMENT = production) nos leerá los nuevos valores:

configuración actualizada appsettings

 

Conclusión

En este post hemos visto que es la configuración en .NET

Cómo acceder y configurar la configuración en nuestras aplicaciones .NET

Diferentes formas de acceder a la configuración en .NET a través de IConfiguration

Convertir la configuración en un objeto

Sobrescribir la configuración por entorno en .NET


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 2024 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café