ASP .NET Core - Usando o Configuration


Hoje vamos recordar como acessar definições e configurações de aplicações ASP .NET Core.

Na ASP .NET Core existem muitas opções que podemos usar para gerenciar definições e configurações quer seja lendo, escrevendo, compondo, filtrando e injetando.

Vamos iniciar entendendo como funciona o ecossistema de configuração na ASP .NET Core.

A configuração de um aplicativo ASP.NET Core é baseada em uma lista de pares nome-valor, coletados em tempo de execução de uma variedade de fontes de dados, onde os arquivos JSON são os mais usados.

Todos os valores carregados são compostos em um único contêiner e os dados coletados podem ter qualquer nível de aninhamento e também podem ser hierárquicos.

O contêiner raiz é um objeto que implementa a interface IConfigurationRoot. Os dados de configuração são normalmente construídos no construtor da classe Startup.

A seguir temos um exemplo de configuração definida na classe Startup de uma aplicação ASP .NET Core:

       public Startup(IWebHostEnvironment env)
        {
            var configApp = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json")
                .Build();
            // Salva a configuração
            Configuration = configApp;
            // proxima tarefa:
            //   - Carregar os dados da configuração em classes
            //   - Compartilhar as classes com o resto da aplicação
        }

Note que estamos injetando uma instância de IWebHostEnvironment no construtor para definir o caminho base para localizar os arquivos JSON usados para preencher a configuração.

A classe ConfigurationBuilder é responsável por agregar valores de configuração e construir o a configuração. Os dados agregados devem ser salvos na classe de inicialização para serem usados posteriormente durante a inicialização do pipeline.

A configuração resulta da combinação de valores que podem vir de várias fontes de dados, e assim podemos ter outros arquivos que podem ser incluídos e acessados da seguinte forma:

    public Startup(IWebHostEnvironment env)
    {
         var configApp = new ConfigurationBuilder()
           .SetBasePath(env.ContentRootPath)
           .AddJsonFile("appsettings.json")
           .AddJsonFile("appsecreto.json")
           .AddJsonFile("meuapp.json")
           .AddEnvironmentVariables()
           .AddInMemoryCollection(
                 new Dictionary<string, string> { {"Timezone", "+1"}
        })
       .Build();
       ...
    }

A configuração é preenchida de forma progressiva e assim os provedores de dados são chamados na mesma sequência em que são concatenados ao construtor de configuração e cada um tem a chance de substituir as entradas configuradas anteriormente. Se a mesma entrada, digamos fuso horário, for fornecida por vários provedores, o último sobrepõe o valor anterior.

Você pode importar configurações de qualquer número de arquivos JSON, e cada arquivo JSON pode ter sua própria estrutura. Observe que os arquivos JSON oferecem suporte a uma convenção de nomenclatura especial que segue a seguinte notação:


    file-name.[Environment].json
 

Onde [Environment] refere-se ao nome do ambiente atual.

O espaço reservado [Environment] se refere ao nome do ambiente atual. Assim, se a aplicação estiver sendo executada no modo de Desenvolvimento, um arquivo vinculado à árvore de configuração com o nome a seguir será carregado:


    appsettings.development.json
 

Naturalmente se você definir a carga de um arquivo JSON no Startup e o arquivo não existir será lançada uma exceção. Entretanto você pode tornar a carga do arquivo opcional usando a seguinte sintaxe:

       public Startup(IWebHostEnvironment env)
        {
            var configApp = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional : true)
                .Build();
           ....
        }

Acessando a configuração 

Para ler dados de configuração via código, você pode usar uma sintaxe de indexador e especificar uma string de caminho que não diferencia maiúsculas de minúsculas e que aponta para as informações que você deseja ler.

Para delimitar propriedades em um esquema hierárquico, você usa o símbolo de dois pontos (:).

Por exemplo, considere o seguinte arquivo JSON:

{
 "Data": {
   "DefaultConnection": "Data Source=\\sqlexpress;Initial Catalog=Teste;Integrated Security=True"
},
 "AppSettings": {
         "Factory": "sqlclient",
         "FactoryType": "AbstractFactory.Classes.SqlClientFactory",
         "Sorting" :  "true",
         "PageSize" : "10"
  }
}

Para acessar o valor de DefaultConnection podemos fazer assim:

 var connection = Configuration["Data:DefaultConnection"];

Usando este formato o valor será retornado no formato string.

Existem ainda as seguintes opções para poder acessar e ler dados da configuração:

1- GetValue<tipo>


 var pageSize = Configuration.GetValue<int>["AppSettings:PageSize"];

Acima usamos a API fortemente tipada GetValue<tipo> que retorna o valor do tipo informado.

2- GetSection


 var demo = Configuration.GetSection("AppSettings").GetValue<bool>("Sorting");
 

O método GetSection permite que selecionar uma sub-árvore de configuração inteira onde você pode agir usando o indexador e a API fortemente tipada GetValue<tipo> para obter o valor no formato desejado.

Todos esses métodos de acesso direto na configuração funcionam independentemente da origem dos dados, seja JSON, ou seja na memória, ou na linha de comando, etc.

Carregando dados de configuração em uma classe

Para carregar dados da configuração em uma classe na ASP .NET Core podemos usar o método Bind no objeto raiz de configuração.

Assim suponha que você tenha o seguinte arquivo de configuração definido em appsettings.json:

{
 "WebConfig":
  {
      "MailServer": "mail.  site.com",
      "MailServerUsername": "macoratti@teste.com",
      "MailServerPassword": "numsey",
      "SenderEmail": "admin@site.com",
      "SenderName": "Macoratti",
      "AdminSenderEmail": "admin@email.com"
  }
}

Podemos definir uma classe chamada EmailConfiguration com as seguintes propriedades:

    public class EmailConfiguration
    {
        public string MailServer { get; set; }
        public string MailServerUsername { get; set; }
        public string MailServerPassword { get; set; }
        public string SenderName { get; set; }
        public string SenderEmail { get; set; }
        public string AdminSenderEmail { get; set; }
    }

A agora podemos usar o método Bind para obter os valores de configuração e preencher a instância da classe EmailConfiguration:

public void ConfigureServices(IServiceCollection services)
{
    var config = new EmailConfiguration();
    Configuration.Bind("WebConfig", config);

    services.AddSingleton(config);
    services.AddControllers();
}

Assim temos uma instância de objeto de configuração preenchida que tem valores definidos no arquivo de configuração. 

Depois que a classe de configuração POCO personalizada for preenchida, você pode executar seu próprio código de validação para garantir que apenas as configurações válidas sejam recebidas.

Depois disso eu posso pegar o objeto configurado e adicioná-lo ao provedor de DI como um singleton. E então, posso injetar a instância bruta da configuração em outros componentes ou views :

services.AddSingleton(config);

Para injetar esse singleton de configuração adicionado acima agora é simplesmente uma questão de solicitar o EmailConfiguration no construtor de qualquer classe que precise dele, conforme mostrado a seguir:

   [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };
        private readonly ILogger<WeatherForecastController> _logger;
        EmailConfiguration ConfigEmail { get; }
        public WeatherForecastController(ILogger<WeatherForecastController> logger,
                  EmailConfiguration emailConfig)
        {
            _logger = logger;
            ConfigEmail = emailConfig;
        }
        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            var teste = ConfigEmail.SenderName;
            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }

Vimos assim como é simples gerenciar as configurações em aplicações ASP .NET Core.

E estamos conversados...

Pegue o projeto aqui: Api_Configuration.zip

"E, se não há ressurreição de mortos, também Cristo não ressuscitou.
E, se Cristo não ressuscitou, logo é vã a nossa pregação, e também é vã a vossa fé."
1 Coríntios 15:13,14

Referências:


José Carlos Macoratti