Jak uzyskać dostęp do konfiguracji w dowolnej klasie w ASP.NET Core?

127

Przejrzałem dokumentację konfiguracji dotyczącą ASP.NET core. Dokumentacja mówi, że możesz uzyskać dostęp do konfiguracji z dowolnego miejsca w aplikacji.

Poniżej znajduje się plik Startup.cs utworzony przez szablon

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

Więc Startup.cskonfigurujemy wszystkie ustawienia, Startup.cs ma również właściwość o nazwieConfiguration

Czego nie jestem w stanie zrozumieć, jak uzyskujesz dostęp do tej konfiguracji w kontrolerze lub w dowolnym miejscu w aplikacji? MS zaleca użycie wzorca opcji, ale mam tylko 4-5 par klucz-wartość, więc nie chciałbym używać wzorca opcji. Chciałem tylko mieć dostęp do konfiguracji w aplikacji. Jak wstrzyknąć to w dowolnej klasie?

LP13
źródło
1
Jeśli jest to 4-5 par kluczowych wartości, możesz po prostu wstrzyknąć te indywidualne ustawienia. Poleciłbym to podejście lub wzorzec opcji do celów testowalności. Wszystkie trzy metody (w tym ta, o którą pierwotnie pytałeś) są wymienione jako odpowiedzi w następującym możliwym zduplikowanym pytaniu: stackoverflow.com/questions/30263681/…
stephen.vakil
Aby uzyskać dostęp do konfiguracji jako słownika z dowolnego miejsca, zaznacz tę odpowiedź .
Amro
Sprawdź tutaj pełny przykład kodu.
Arghya C
Jeśli przyjeżdżasz tutaj, ponieważ nie możesz się doczekać konwersji konfiguracji Framework do konfiguracji CORE, ta odpowiedź jest dla Ciebie stackoverflow.com/a/56498687/1704458
TS,

Odpowiedzi:

148

Aktualizacja

Użycie ASP.NET Core 2.0 spowoduje automatyczne dodanie IConfigurationwystąpienia aplikacji w kontenerze iniekcji zależności. Działa to również w połączeniu z ConfigureAppConfigurationnaWebHostBuilder .

Na przykład:

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

    host.Run();
}

Jest to tak proste, jak dodanie IConfigurationinstancji do kolekcji usług jako pojedynczego obiektu w ConfigureServices:

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

   // ...
}

Gdzie Configurationjest instancja w twojej Startupklasie.

Pozwala to na wprowadzenie IConfigurationdowolnego kontrolera lub usługi:

public class HomeController
{
   public HomeController(IConfiguration configuration)
   {
      // Use IConfiguration instance
   }
}
Henk Mollema
źródło
4
Mollerna… a co powiesz na to, że chcesz wstrzyknąć konfigurację w oddzielnym projekcie biblioteki klas w rozwiązaniu? Próbowałem tak jak ta prywatna statyczna IConfiguration _configuration {get; zestaw; } public DatabaseHelpers (konfiguracja IConfiguration) {_configuration = configuration; } ale _configuration jest zawsze zerowa ... nigdy nie zostanie trafiona w konstruktor
dinotom
2
To powiedziawszy, przechodzenie w IConfigurationten sposób jest bardzo nieszczelne. O wiele lepiej jest użyć wzorca Options .
Marc L.
7
Jak uzyskać dostęp do wartości z „appsettings.json” bezpośrednio w niestandardowej klasie? Bez przekazywania danych od administratora? Czy to możliwe?
Tadej
2
@HenkMollema Czy możesz dodać tutaj przykład? Jak bym wstrzyknął to do dowolnej klasy (skąd?).
Tadej
5
@HenkMollema Pytanie brzmiało, jak wstrzyknąć do dowolnej klasy ... a nie jak wstrzyknąć do „dowolnej klasy rozwiązanej przez wstrzykiwanie zależności”. Myślę, że to jest błąd w komunikacji ... jego klasa prawdopodobnie nie jest wywoływana z łańcucha, który zaczyna się od kontrolera lub innego obiektu rozwiązanego automatycznie przez automatyczny proces DI.
BVernon
34

Właściwy sposób, aby to zrobić:

W .NET Core możesz wstrzyknąć IConfigurationparametr jako parametr do konstruktora klasy i będzie on dostępny.

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

Teraz, gdy chcesz utworzyć instancję swojej klasy, ponieważ twoja klasa zostanie wstrzyknięta IConfiguration, nie będziesz w stanie tego zrobić new MyClass(), ponieważ wymaga ona IConfigurationparametru wstrzykniętego do konstruktora, więc będziesz musiał wstrzyknąć swoją klasę jako dobrze do łańcucha wtrysku, co oznacza dwa proste kroki:

1) Dodaj swoje klasy (klasy) - gdzie chcesz użyć IConfiguration, do metody IServiceCollectionat ConfigureServices()wStartup.cs

services.AddTransient<MyClass>();

2) Zdefiniuj instancję - powiedzmy w Controller, i wstrzyknij ją za pomocą konstruktora:

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

Teraz powinieneś móc _myClass.configurationswobodnie cieszyć się ...

Inna opcja:

Jeśli nadal szukasz sposobu na udostępnienie go bez konieczności wstrzykiwania klas do kontrolera, możesz zapisać go w pliku static class, który skonfigurujesz wStartup.cs , coś takiego:

public static class MyAppData
{
    public static IConfiguration Configuration;
}

Twój Startupkonstruktor powinien wyglądać tak:

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

Następnie użyj MyAppData.Configuration dowolnego miejsca w programie.

Nie konfrontuj mnie, dlaczego pierwsza opcja jest właściwa, widzę tylko, że doświadczeni programiści zawsze unikają danych śmieciowych po drodze i dobrze rozumiemy, że nie jest najlepszą praktyką, aby mieć mnóstwo danych dostępnych w pamięci przez cały czas, nie jest to również dobre dla wydajności ani dla rozwoju, a być może bezpieczniej jest mieć tylko to, czego potrzebujesz.

Mayer Spitzer
źródło
5
Całe to wstrzykiwanie plików konfiguracyjnych wydaje się bezcelowe / nieporządne. TY dla koncepcji statycznej klasy konfiguracji.
Andrew,
1
Pytanie oczywiście dotyczyło dostępu do konfiguracji w dowolnej klasie, a nie tylko kontrolera. I chociaż można pomyśleć o nowym rozwoju usług lean (mikrousług), w przypadku migracji jest to duży ból. Dlatego firma Microsoft System.Configurationwróciła do CORE. Teraz możesz uzyskać dostęp do starych, dobrych app.configs, tak jak za dawnych dobrych czasów. I nie mówię tutaj o kontrolerach. Mówimy o komponentach, które mają własne konfiguracje
TS,
Umożliwia dostęp w dowolnej klasie, a nie tylko w kontrolerze, wymaga po prostu zaimportowania do kontrolera w celu uzyskania iniekcji zależności.
Mayer Spitzer
1
Obie metody działają, a argumenty za lub przeciw każdej z nich są moim zdaniem akademickie. Używałem obu do różnych zastosowań ... teraz, dzięki twojej niezwykle łatwej drugiej opcji. Tworzenie klasy statycznej jest dość trudne przy użyciu DI.
iGanja
Druga metoda pomaga również w przypadku typowego problemu w .Net Core 2.0 - obiektów tworzonych jako parametr POST (tj. Automatycznie deserializowanych z JSON), gdzie nie masz możliwości wstrzyknięcia do konstruktora (przynajmniej nie bez dodatkowego kodu). To świetnie sprawdza się w tym scenariuszu
Joe Moon
30

Wiem, że to jest stare, ale biorąc pod uwagę wzorce IOptions, jest stosunkowo proste do zaimplementowania:

  1. Klasa z publicznymi właściwościami get / set, które pasują do ustawień w konfiguracji

    public class ApplicationSettings
    {
        public string UrlBasePath { get; set; }
    }
  2. zarejestruj swoje ustawienia

    public void ConfigureServices(IServiceCollection services)
    {
     ...
     services.Configure<ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
    ...
    }
  3. wstrzyknąć przez IOptions

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

Nie jestem pewien, dlaczego po prostu tego nie zrobiłeś.

ScottC
źródło
2
Jak uzyskać dostęp do wartości z „appsettings.json” bezpośrednio w niestandardowej klasie?
Tadej
2
@JedatKinports musisz dodać zależności Nuget Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.Bindera Microsoft.Extensions.Configuration.Jsonnastępnie załadować appsettings.jsonplik taki jak ... a var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();także upewnić się, że appsettings.jsonkopiowanie do katalogu wyjściowego jest ustawione nacopy always
LP13
7

Istnieje również opcja configurationstatyczna w pliku startup.cs, dzięki czemu zmienne statyczne są wygodne, jeśli masz do nich łatwy dostęp, hę!

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

internal static IConfiguration Configuration { get; private set; }

Dzięki temu konfiguracja jest dostępna w dowolnym miejscu przy użyciu Startup.Configuration.GetSection...Co może pójść nie tak?

konzo
źródło
6

W tej chwili robię to tak:

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

A potem używam tego, gdzie muszę pobrać dane z pliku appsettings.json:

var appSettingsJson = AppSettingsJson.GetAppSettings();
// appSettingsJson["keyName"]
Tadej
źródło
3

Zajrzałem do próbki wzoru opcji i zobaczyłem to:

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

Dodając Iconfiguration w konstruktorze mojej klasy, mogłem uzyskać dostęp do opcji konfiguracyjnych przez DI.

Przykład:

public class MyClass{

    private Iconfiguration _config;

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

    ... // access _config["myAppSetting"] anywhere in this class
}
Pieter Heemeryck
źródło
Czy to działa bez wyraźnego wspominania o MyClass w Startup.cs, coś takiego? services.AddTransient <MyClass> ();
Ojciec chrzestny
Tak, właściwie powinieneś wspomnieć o klasach, które chcesz wstrzyknąć w Startup.cs, a nie na odwrót. Ale IConfiguration jest, jak sądzę, domyślnie już dostępny do wstrzyknięcia.
Pieter Heemeryck
Tak to działa. Próbowałem tego po skomentowaniu, a implementacja konfiguracji została wstrzyknięta w IConfiguration. Dzięki tak czy siak :)
Ojciec chrzestny
1
@netfed Jak stwierdza Mayer Spitzer w swojej odpowiedzi, oczywiście będziesz musiał dodać MyClass do uruchamiania i wstrzyknąć go tam, gdzie potrzebujesz, więc nie musisz samodzielnie tworzyć nowej instancji MyClass, wstrzykujesz ją tam, gdzie jej potrzebujesz.
Pieter Heemeryck
2

Wiem, że może być na to kilka sposobów, używam Core 3.1 i szukałem optymalnej / czystszej opcji i skończyło się na tym:

  1. Moja klasa startowa jest domyślnie
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. Mój plik appsettings.json wygląda tak
{
  "CompanySettings": {
    "name": "Fake Co"
  }
}
  1. Moja klasa jest kontrolerem API, więc najpierw dodałem odwołanie using, a następnie wstrzyknąłem interfejs IConfiguration
using Microsoft.Extensions.Configuration;

public class EmployeeController 
{
    private IConfiguration _configuration;
    public EmployeeController(IConfiguration configuration)
    {
        _configuration = configuration;
    }
}
  1. Wreszcie użyłem metody GetValue
public async Task<IActionResult> Post([FromBody] EmployeeModel form)
{
    var companyName = configuration.GetValue<string>("CompanySettings:name");
    // companyName = "Fake Co"
}
user1058637
źródło
0

W 8-2017 Microsoft wypuścił System.Configurationdla .NET CORE v4.4. Obecnie wersja zapoznawcza wersji 4.5 i 4.6 .

Dla tych z nas, którzy pracują nad transformacją z .Net Framework do CORE, jest to niezbędne. Umożliwia przechowywanie i używanie aktualnych app.configplików, do których można uzyskać dostęp z dowolnego zestawu. Prawdopodobnie może to być nawet alternatywa dlaappsettings.json , ponieważ Microsoft zdał sobie sprawę z takiej potrzeby. Działa tak samo jak wcześniej w FW. Jest jedna różnica:

W aplikacjach internetowych [np. ASP.NET CORE WEB API] musisz używać, app.configa nie web.config dla swojego appSettingslub configurationSection. Może być konieczne użycie, web.configale tylko w przypadku wdrażania witryny za pośrednictwem usług IIS. Umieszczasz ustawienia specyficzne dla usług IIS w programieweb.config

Przetestowałem to z netstandard20 DLL i Asp.net Core Web Api i wszystko działa.

TS
źródło
0

Najlepszym sposobem jest użycie wzorca opcji w ASP.NET Core . Chcę tylko dodać, jeśli potrzebujesz dostępu do opcji w swoim startup.cs , polecam zrobić to w ten sposób:

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
źródło
więc jeśli mam całą podsekcję z kilkunastoma wartościami konfiguracyjnymi, do których potrzebuję dostępu w ConfigureServices, muszę to zrobić dla nich wszystkich? Czy nie ma innego sposobu, aby to zrobić za pomocą wzorca IOptions? Muszę wstrzyknąć to do statycznej metody rozszerzenia, w której konfiguruję mój autobus komunikacji zbiorowej. A co z sugestią firmy Microsoft dotyczącą niestosowania wzorca IOptions w programie ConfigureServices docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/ ...
kuldeep
Nie używam IOPtions w ConfigureService tutaj ...
Martin Brandl
-3

Muszę odczytać własne parametry przy starcie.
To musi być tam przed uruchomieniem WebHost (ponieważ potrzebuję adresu URL / IP „do nasłuchiwania” i portu z pliku parametrów i zastosowania go do hosta WebHost). Ponadto potrzebuję ustawień publicznych w całej aplikacji.

Po dłuższym wyszukiwaniu (nie znaleziono pełnego przykładu, tylko fragmenty) i po różnych próbach i błędach, zdecydowałem się zrobić to w „stary sposób” z własnym plikiem .ini.
Więc ... jeśli chcesz użyć swojego mieć własny plik .ini i / lub ustaw "do nasłuchiwania url / IP" swoje własne i / lub potrzebujesz publicznych ustawień, to jest dla ciebie ...

Kompletny przykład, ważny dla rdzenia 2.1 (MVC):

Utwórz plik .ini - przykład:

[Startup]
URL = http://172.16.1.201:22222
[Parametr]
* Dummy1 = gew7623
Dummy1 = true
Dummy2 = 1

przy czym Dummyx są uwzględniane tylko jako przykład dla innych typów dat niż string (a także do testowania przypadku „zły parametr” (patrz kod poniżej).

Dodano plik kodu w katalogu głównym projektu do przechowywania zmiennych globalnych:

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

Zmieniono kod w program.cs (przed 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

    }
}

Tą drogą:

  • Moja konfiguracja aplikacji jest oddzielona od pliku appsettings.json i nie mam żadnych skutków ubocznych, których należy się obawiać, jeśli MS wprowadzi zmiany w przyszłych wersjach ;-)
  • Mam swoje ustawienia w zmiennych globalnych
  • Jestem w stanie ustawić „adres URL do nasłuchiwania” dla każdego urządzenia, na którym działa aplikacja (moja maszyna deweloperska, serwer intranetowy i serwer internetowy)
  • Jestem w stanie dezaktywować ustawienia, stary sposób (po prostu ustaw * przed)
  • Jestem w stanie zareagować, jeśli coś jest nie tak w pliku .ini (np.
    Niezgodność typów) Jeśli - np. - ustawiony jest niewłaściwy typ (np. * Dummy1 = gew7623 jest aktywowany zamiast Dummy1 = true) host wyświetla kolor czerwony informacje są na konsoli (w tym wyjątek) i mogę zareagować również w aplikacji (GV.bFehler_Ini jest ustawione na true, jeśli wystąpią błędy z .ini)
FredyWenger
źródło