Como acesso a Configuração em qualquer classe no ASP.NET Core?

127

Passei pela documentação de configuração no núcleo do ASP.NET. A documentação diz que você pode acessar a configuração de qualquer lugar do aplicativo.

Abaixo está Startup.cs criado pelo modelo

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

        if (env.IsEnvironment("Development"))
        {
            // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
            builder.AddApplicationInsightsSettings(developerMode: true);
        }

        builder.AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container
    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddApplicationInsightsTelemetry(Configuration);

        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        app.UseApplicationInsightsRequestTelemetry();

        app.UseApplicationInsightsExceptionTelemetry();

        app.UseMvc();
    }
}

Portanto Startup.cs, ao definir todas as configurações, o Startup.cs também possui uma propriedade chamadaConfiguration

O que não consigo entender como você acessa essa configuração no controlador ou em qualquer lugar do aplicativo? A Microsoft está recomendando o uso de padrão de opções, mas eu tenho apenas 4-5 pares de valores-chave, portanto, gostaria de não usar o padrão de opções. Eu só queria ter acesso à configuração no aplicativo. Como injeto em qualquer classe?

LP13
fonte
1
Se houver 4-5 pares de valores-chave, basta injetar essas configurações individuais. Eu recomendaria essa abordagem ou o padrão de opções para fins de testabilidade. Todos os três métodos (incluindo o que você perguntou originalmente) estão listados como respostas na seguinte pergunta duplicada possível: stackoverflow.com/questions/30263681/…
stephen.vakil
Para acessar a configuração como um dicionário de qualquer lugar, verifique esta resposta .
Amr
Confira aqui o exemplo de código completo.
Arghya C
Se você vir aqui , porque você está disperso com a conversão de configuração Framework para configuração do núcleo, esta resposta é para você stackoverflow.com/a/56498687/1704458
TS

Respostas:

147

Atualizar

O uso do ASP.NET Core 2.0 adicionará automaticamente a IConfigurationinstância do seu aplicativo no contêiner de injeção de dependência. Isso também funciona em conjunto com ConfigureAppConfigurationo WebHostBuilder.

Por exemplo:

public static void Main(string[] args)
{
    var host = WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration(builder =>
        {
            builder.AddIniFile("foo.ini");
        })
        .UseStartup<Startup>()
        .Build();

    host.Run();
}

É tão fácil quanto adicionar a IConfigurationinstância à coleção de serviços como um objeto singleton em ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
   services.AddSingleton<IConfiguration>(Configuration);

   // ...
}

Onde Configurationestá a instância na sua Startupclasse.

Isso permite que você injete IConfigurationqualquer controlador ou serviço:

public class HomeController
{
   public HomeController(IConfiguration configuration)
   {
      // Use IConfiguration instance
   }
}
Henk Mollema
fonte
4
Mollerna .... e se você quiser injetar a configuração em um projeto de biblioteca de classes separado na solução? Tentei como este IConfiguration estático privado _configuration {get; conjunto; } public DatabaseHelpers (configuração IConfiguration) {_configuration = configuration; } Mas _configuration é sempre nulo ... nunca é atingido na contstructor
dinotom
2
Dito isto, passar IConfigurationassim é muito gotejante. Muito melhor usar o padrão Opções .
Marc L.
7
Como acessar os valores do "appsettings.json" diretamente em uma classe personalizada? Sem passar os dados de um controlador? É possível?
Tadej
2
@HenkMollema Você poderia adicionar um exemplo disso aqui? Como eu o injetaria em qualquer classe (de onde?).
Tadej 27/07
5
@HenkMollema A questão era como injetar em qualquer classe ... não como injetar em "qualquer classe resolvida por injeção de dependência". Eu acho que é aí que está a falta de comunicação ... sua classe provavelmente não está sendo chamada de uma cadeia que começa com um Controller ou outro objeto resolvido automaticamente pelo processo de DI automático.
BVernon
34

A maneira correta de fazer isso:

No .NET Core, você pode injetar o IConfigurationparâmetro como no construtor Class, e ele estará disponível.

public class MyClass 
{
    private IConfiguration configuration;
    public MyClass(IConfiguration configuration)
    {
        ConnectionString = new configuration.GetValue<string>("ConnectionString");
    }

Agora, quando você deseja criar uma instância de sua classe, uma vez que sua classe é injetada IConfiguration, você não poderá fazer isso new MyClass(), porque ela precisa de um IConfigurationparâmetro injetado no construtor; portanto, será necessário injetar sua classe como bem à cadeia de injeção, o que significa duas etapas simples:

1) Adicionar sua classe / es - onde você deseja usar o IConfiguration, ao IServiceCollectionno ConfigureServices()métodoStartup.cs

services.AddTransient<MyClass>();

2) Defina uma instância - digamos no Controller, e injete-a usando o construtor:

public class MyController : ControllerBase
{
    private MyClass _myClass;
    public MyController(MyClass myClass)
    {
        _myClass = myClass;
    }

Agora você deve poder desfrutar _myClass.configurationlivremente ...

Outra opção:

Se você ainda estiver procurando uma maneira de disponibilizá-lo sem precisar injetar as classes no controlador, poderá armazená-lo em um static class, que você configurará no Startup.cs, algo como:

public static class MyAppData
{
    public static IConfiguration Configuration;
}

E seu Startupconstrutor deve ficar assim:

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
    MyAppData.Configuration = configuration;
}

Em seguida, use MyAppData.Configurationem qualquer lugar do seu programa.

Não me pergunte por que a primeira opção é o caminho certo, posso ver desenvolvedores experientes sempre evitarem dados de lixo ao longo do caminho e é bem compreendido que não é a melhor prática ter muitos dados disponíveis na memória o tempo todo, nem é bom para desempenho e nem para desenvolvimento, e talvez também seja mais seguro ter apenas com você o que você precisa.

Mayer Spitzer
fonte
5
Toda essa injeção de arquivos de configuração parece meio inútil / confusa. TY para a ideia de classe de configuração estática.
Andrew Andrew
1
A questão, é claro, era sobre o acesso à configuração em qualquer classe e não apenas no controlador. E, embora com o novo desenvolvimento de serviços lean (microsserviços), isso possa ser pensado, quando se trata de migrações, isso é uma grande dor. É por isso que a Microsoft conseguiu o System.ConfigurationCORE de volta aos trilhos. Agora, você pode acessar seu bom e velho app.configs, como nos bons e velhos tempos. E eu não estou falando de controladores aqui. Estamos falando de componentes que têm suas próprias configurações
TS
Ele permite o acesso em qualquer classe e não apenas no controlador, apenas requer que seja importado para o controlador para obter injeção de dependência.
Mayer Spitzer
1
Qualquer método funciona, e os argumentos a favor ou contra cada um são acadêmicos na minha opinião. Eu usei ambos para diferentes aplicações ... agora, graças à sua segunda opção extremamente fácil. Criar uma classe estática é bastante difícil usando o DI.
IGanja 29/08/19
O segundo método também ajuda com um problema comum no .Net Core 2.0 - objetos instanciados como um parâmetro POST (ou seja, desserializado automaticamente do JSON), onde você não tem a oportunidade de injetar no construtor (pelo menos não sem muito de código extra). Isso funciona muito bem para esse cenário
Joe Moon
30

Eu sei que isso é antigo, mas, devido aos padrões de IOptions, é relativamente simples de implementar:

  1. Classe com propriedades públicas de obtenção / configuração que correspondem às configurações na configuração

    public class ApplicationSettings
    {
        public string UrlBasePath { get; set; }
    }
  2. registre suas configurações

    public void ConfigureServices(IServiceCollection services)
    {
     ...
     services.Configure<ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
    ...
    }
  3. injetar via IOptions

    public class HomeController
    {
       public HomeController(IOptions<ApplicationSettings> appSettings)
       { ...
        appSettings.Value.UrlBasePath
        ...
        // or better practice create a readonly private reference
        }
     }

Não sei por que você não faria isso.

ScottC
fonte
2
Como acessar os valores do "appsettings.json" diretamente em uma classe personalizada?
Tadej
2
@JedatKinports você precisa adicionar dependências Nuget Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.Bindere Microsoft.Extensions.Configuration.Jsonem seguida, você carregar appsettings.jsonarquivos como var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();..e também você tem que certificar-se de appsettings.jsoncópia ao diretório de saída está definido paracopy always
LP13
7

Há também uma opção para tornar configurationestática no startup.cs, para que você possa acessá-lo em qualquer lugar com facilidade, variáveis ​​estáticas sejam convenientes, hein!

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

internal static IConfiguration Configuration { get; private set; }

Isso torna a configuração acessível em qualquer lugar usando Startup.Configuration.GetSection...O que pode dar errado?

Konzo
fonte
6

Estou fazendo assim no momento:

// Requires NuGet package Microsoft.Extensions.Configuration.Json

using Microsoft.Extensions.Configuration;
using System.IO;

namespace ImagesToMssql.AppsettingsJson
{
    public static class AppSettingsJson
    {           
        public static IConfigurationRoot GetAppSettings()
        {
            string applicationExeDirectory = ApplicationExeDirectory();

            var builder = new ConfigurationBuilder()
            .SetBasePath(applicationExeDirectory)
            .AddJsonFile("appsettings.json");

            return builder.Build();
        }

        private static string ApplicationExeDirectory()
        {
            var location = System.Reflection.Assembly.GetExecutingAssembly().Location;
            var appRoot = Path.GetDirectoryName(location);

            return appRoot;
        }
    }
}

E então eu uso isso onde preciso obter os dados do arquivo appsettings.json:

var appSettingsJson = AppSettingsJson.GetAppSettings();
// appSettingsJson["keyName"]
Tadej
fonte
3

Eu olhei para o exemplo de padrão de opções e vi o seguinte:

public class Startup
{
    public Startup(IConfiguration config)
    {
        // Configuration from appsettings.json has already been loaded by
        // CreateDefaultBuilder on WebHost in Program.cs. Use DI to load
        // the configuration into the Configuration property.
        Configuration = config;
    }
...
}

Ao adicionar Iconfiguration no construtor da minha classe, eu poderia acessar as opções de configuração através do DI.

Exemplo:

public class MyClass{

    private Iconfiguration _config;

    public MyClass(Iconfiguration config){
        _config = config;
    }

    ... // access _config["myAppSetting"] anywhere in this class
}
Pieter Heemeryck
fonte
Funciona sem mencionar explicitamente MyClass no Startup.cs, algo parecido com isto? services.AddTransient <MyClass> ();
O padrinho
Sim, na verdade, você deve mencionar as classes que deseja injetar no Startup.cs, e não o contrário. Mas, por padrão, a IConfiguration já está disponível para injeção.
Pieter Heemeryck
Sim, funciona. Eu tentei isso depois de fazer o comentário e a implementação da configuração foi injetada no IConfiguration. Obrigado de qualquer maneira :)
O Poderoso Chefão
1
@netfed Como Mayer Spitzer declara em sua resposta, é claro que você precisará adicionar o MyClass à inicialização e injetá-lo onde quer que precise, para não criar uma nova instância do MyClass, injetá-lo onde for necessário.
Pieter Heemeryck
2

Eu sei que pode haver várias maneiras de fazer isso, estou usando o Core 3.1 e estava procurando a opção ideal / limpa e acabei fazendo o seguinte:

  1. Minha classe de inicialização é como padrão
public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
}
  1. Meu appsettings.json é assim
{
  "CompanySettings": {
    "name": "Fake Co"
  }
}
  1. Minha classe é uma API Controller, então, primeiro adicionei a referência de uso e, em seguida, injetei a interface IConfiguration
using Microsoft.Extensions.Configuration;

public class EmployeeController 
{
    private IConfiguration _configuration;
    public EmployeeController(IConfiguration configuration)
    {
        _configuration = configuration;
    }
}
  1. Finalmente, usei o método GetValue
public async Task<IActionResult> Post([FromBody] EmployeeModel form)
{
    var companyName = configuration.GetValue<string>("CompanySettings:name");
    // companyName = "Fake Co"
}
user1058637
fonte
0

Em 8-2017, a Microsoft lançou o System.Configuration.NET CORE v4.4. Atualmente visualização v4.5 e v4.6.

Para aqueles de nós que trabalham na transformação do .Net Framework para o CORE, isso é essencial. Permite manter e usar app.configarquivos atuais , que podem ser acessados ​​a partir de qualquer montagem. Provavelmente é até uma alternativa appsettings.json, já que a Microsoft percebeu a necessidade. Funciona da mesma forma que antes no FW. Há uma diferença:

Nos aplicativos da web, [por exemplo, API do ASP.NET CORE WEB], é necessário usar app.confige não o web.config para o seu appSettingsou configurationSection. Pode ser necessário usá-lo, web.configmas apenas se você estiver implantando seu site via IIS. Você coloca configurações específicas do IIS emweb.config

Eu testei com netstandard20 DLL e Asp.net Core Web Api e está tudo funcionando.

TS
fonte
0

Usar o padrão Opções no ASP.NET Core é o caminho a seguir. Eu só quero adicionar, se você precisar acessar as opções no seu startup.cs , recomendo fazer o seguinte:

CosmosDbOptions.cs:

public class CosmosDbOptions
{
    public string ConnectionString { get; set; }
}

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    // This is how you can access the Connection String:
    var connectionString = Configuration.GetSection(nameof(CosmosDbOptions))[nameof(CosmosDbOptions.ConnectionString)];
}
Martin Brandl
fonte
Então, se eu tenho uma subseção inteira com uma dúzia de valores de configuração que preciso acessar no ConfigureServices, preciso fazer isso para todos eles? Não existe outra maneira de fazer isso via padrão IOptions? Preciso injetar isso em um método de extensão estática em que estou configurando meu barramento de transporte de massa. Também sobre esta sugestão da Microsoft sobre não usar o padrão IOptions no ConfigureServices docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/…
kuldeep
Eu não uso IOPtions no ConfigureService aqui ...
Martin Brandl
-3

Eu tenho que ler os próprios parâmetros na inicialização.
Isso precisa estar lá antes que o WebHost seja iniciado (como eu preciso da URL / IP "to listen" e da porta do arquivo de parâmetro e aplique-o ao WebHost). Além disso, preciso das configurações públicas em todo o aplicativo.

Depois de pesquisar por um tempo (nenhum exemplo completo encontrado, apenas trechos) e depois de várias tentativas e erros, decidi fazê-lo da “maneira antiga” com um arquivo .ini.
Então, se você quiser usar o seu próprio arquivo .ini e / ou defina o "para escutar URL / IP" seu e / ou precise das configurações públicas, isto é para você ...

Exemplo completo, válido para o núcleo 2.1 (mvc):

Crie um arquivo .ini - exemplo:

[Inicialização]
URL = http://172.16.1.201:22222
[Parâmetro]
* Dummy1 = gew7623
Dummy1 = true
Dummy2 = 1

em que o Dummyx é incluído apenas como exemplo para outros tipos de datas que não a string (e também para testar o caso "parâmetro errado" (consulte o código abaixo).

Adicionado um arquivo de código na raiz do projeto, para armazenar as variáveis ​​globais:

namespace MatrixGuide
{
    public static class GV
    {
        // In this class all gobals are defined

        static string _cURL;
        public static string cURL // URL (IP + Port) on that the application has to listen
        {
            get { return _cURL; }
            set { _cURL = value; }
        }

        static bool _bdummy1;
        public static bool bdummy1 // 
        {
            get { return _bdummy1; }
            set { _bdummy1 = value; }
        }

        static int _idummy1;
        public static int idummy1 // 
        {
            get { return _idummy1; }
            set { _idummy1 = value; }
        }

        static bool _bFehler_Ini;
        public static bool bFehler_Ini // 
        {
            get { return _bFehler_Ini; }
            set { _bFehler_Ini = value; }
        }

        // add further  GV variables here..
    }
    // Add further classes here... 
}

O código foi alterado em program.cs (antes de CreateWebHostBuilder ()):

namespace MatrixGuide
{
    public class Program
    {
        public static void Main(string[] args)
        {
            // Read .ini file and overtake the contend in globale
            // Do it in an try-catch to be able to react to errors
            GV.bFehler_Ini = false;
            try
            {
                var iniconfig = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddIniFile("matrixGuide.ini", optional: false, reloadOnChange: true)
                .Build();
                string cURL = iniconfig.GetValue<string>("Startup:URL");
                bool bdummy1 = iniconfig.GetValue<bool>("Parameter:Dummy1");
                int idummy2 = iniconfig.GetValue<int>("Parameter:Dummy2");
                //
                GV.cURL = cURL;
                GV.bdummy1 = bdummy1;
                GV.idummy1 = idummy2;
            }
            catch (Exception e)
            {
                GV.bFehler_Ini = true;
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("!! Fehler beim Lesen von MatrixGuide.ini !!");
                Console.WriteLine("Message:" + e.Message);
                if (!(e.InnerException != null))
                {
                    Console.WriteLine("InnerException: " + e.InnerException.ToString());
                }

                Console.ForegroundColor = ConsoleColor.White;
            }
            // End .ini file processing
            //
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>() //;
            .UseUrls(GV.cURL, "http://localhost:5000"); // set the to use URL from .ini -> no impact to IISExpress

    }
}

Deste jeito:

  • Minha configuração de aplicativo é separada do appsettings.json e não tenho nenhum efeito colateral, se o MS fizer alterações em versões futuras ;-)
  • Eu tenho minhas configurações em variáveis ​​globais
  • Eu posso definir o "URL de escuta" de cada dispositivo, a execução do aplicativo (minha máquina de desenvolvimento, o servidor da intranet e o servidor da Internet)
  • Consigo desativar as configurações da maneira antiga (basta definir um * antes)
  • Sou capaz de reagir, se algo estiver errado no arquivo .ini (por exemplo, incompatibilidade de tipo)
    Se - por exemplo - um tipo errado estiver definido (por exemplo, * Dummy1 = gew7623 estiver ativado em vez de Dummy1 = true), o host mostrará vermelho as informações estão no console (incluindo a exceção) e posso reagir também no aplicativo (GV.bFehler_Ini está definido como true, se houver erros com o .ini)
FredyWenger
fonte