ASP.NET Core: Zugriff auf die Konfiguration über eine statische Klasse

76

Ich möchte eine einfache statische Klasse, die auf das Konfigurationsobjekt zugreift. Alle Konfigurationsinformationen sind bereits aus der Datei appsettings.json in der Startup-Klasse eingelesen. Ich brauche nur einen einfachen Weg, um darauf zuzugreifen. Ist das möglich?

namespace MyNamespace
{
    public static class Config
    {
        public string Username => Configuration["Username"];
        public string Password => Configuration["Password"];
    }
}

Überall sonst in der App:

string username = Config.Username;
string password = Config.Password;
Birdus
quelle
1
Erwägen Sie die Verwendung der Abhängigkeitsinversion im Zusammenhang mit dem Anti-Pattern für Service Locator
Nkosi,
Mit Konfiguration meinen Sie appsettings.json oder app.config?
bruno.almeida
appsettings.json. Wird die Frage aktualisieren.
Birdus
1
Die Verwendung einer statischen Klasse kann für Unit-Tests eine schlechte Praxis sein: stackoverflow.com/a/38107134/2803565
S.Serpooshan
1
Warum eine statische Klasse? Sie können die Konfiguration direkt injizieren oder einen Singleton erstellen
Neville Nazerane

Antworten:

48

Eine etwas kürzere Version nach dem gleichen Prinzip wie oben ...

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

public static IConfiguration StaticConfig { get; private set; }

So verwenden Sie es in einer anderen statischen Klasse:

string connString = Startup.StaticConfig.GetConnectionString("DefaultConnection");
DeanC
quelle
Das Teilen dieser Konfigurationswerte zwischen verschiedenen Klassenbibliotheken ist schwierig. Stellen Sie sich vor, Sie haben eine übergreifende Infrastruktur, die Konfiguration / Einstellungen für mehrere Projekte freigibt?
Junior Mayhé
1
Ich bin verwirrt, warum dies nicht die akzeptierte Antwort ist. Besteht hierfür ein gewisses Risiko? Sieht einfach und funktional aus.
Chaim Eliyah
3
Weil es ein Anti-Muster ist. Sie sollten in der Lage sein, alle benötigten Dinge zu injizieren, damit Sie sie während des Tests ersetzen können. Wenn es sich um eine statische Eigenschaft mit a handelt private setter, wie können Sie diese ersetzen? Sie müssen es publicnur zum Testen machen, was nicht richtig ist.
Serj Sagan
2
Ich glaube nicht, dass es ein Anti-Pattern ist, da IConfiguration Bindemethoden hat, mit denen Sie es aus diesem Grund und mehr an eine Klasse binden können. Ich würde es jedoch nicht als IConfiguration speichern. Das wiederholte Parsen von Strings in ihre realen Typen wie Boolesche Werte und Ints ist ein Anti-Pattern, mit dem ich mich in der Microservice-Welt mehr beschäftige. Sie müssen "StaticConfig" nicht festlegen, wenn Sie Werte zum Testen ändern. Sie können MyConfigObject.MyProperty = 5einfach in Ihrem Test-Setup einstellen und in Bewegung bleiben.
djsoteric
9

Nach langem Suchen funktioniert dies (in ASPNetCore 2.2) für den Zugriff auf die Konfiguration appsettings.json aus einer statischen Klasse, aber aus irgendeinem Grund wird appsettings.development.json nicht mehr richtig geladen, aber es könnte etwas anderes in meinem Projekt sein, das das durcheinander bringt. Der reloadOnChange funktioniert. Als Bonus gibt es auch IHostingEnvironment und IHttpContextAccessor. Während dies funktioniert, habe ich mich kürzlich entschlossen, wieder auf einen DI-Ansatz umzusteigen, um dem Paradigmenwechsel zu folgen, wie andere erwähnt haben.

Hier ist eine von vielen Möglichkeiten, auf DI-Inhalte (einschließlich der Konfiguration) in einer statischen Klasse zuzugreifen:

AppServicesHelper.cs:

public static class AppServicesHelper
{
        static IServiceProvider services = null;

        /// <summary>
        /// Provides static access to the framework's services provider
        /// </summary>
        public static IServiceProvider Services
        {
            get { return services; }
            set
            {
                if (services != null)
                {
                    throw new Exception("Can't set once a value has already been set.");
                }
                services = value;
            }
        }

        /// <summary>
        /// Provides static access to the current HttpContext
        /// </summary>
        public static HttpContext HttpContext_Current
        {
            get
            {
                IHttpContextAccessor httpContextAccessor = services.GetService(typeof(IHttpContextAccessor)) as IHttpContextAccessor;
                return httpContextAccessor?.HttpContext;
            }
        }

        public static IHostingEnvironment HostingEnvironment
        {
            get
            {
                return services.GetService(typeof(IHostingEnvironment)) as IHostingEnvironment;
            }
        }

        /// <summary>
        /// Configuration settings from appsetting.json.
        /// </summary>
        public static MyAppSettings Config
        {
            get
            {
                //This works to get file changes.
                var s = services.GetService(typeof(IOptionsMonitor<MyAppSettings>)) as IOptionsMonitor<MyAppSettings>;
                MyAppSettings config = s.CurrentValue;

                return config;
            }
        }
    }
}

Startup.cs:

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

 public void ConfigureServices(IServiceCollection services)
 {
//...

        services.AddHttpContextAccessor();//For HttpContext.

        // Register the IOptions object
        services.Configure<MyAppSettings>(Configuration.GetSection(nameof(MyAppSettings)));

        //Explicitly register the settings object by delegating to the IOptions object so that it can be accessed globally via AppServicesHelper.
        services.AddSingleton(resolver => resolver.GetRequiredService<IOptionsMonitor<MyAppSettings>>().CurrentValue);
 }

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//...
   AppServicesHelper.Services = app.ApplicationServices;
//...
}

Regler:

public class MyController: Controller
{
   public MyController()
   {
   }

   public MyAppSettings Config => AppServicesHelper.Config;

   public async Task<IActionResult> doSomething()
   {
            testModel tm = await myService.GetModel(Config.Setting_1);
            return View(tm);
   }
}

Eine andere Klassenbibliothek:

public static class MyLibraryClass
{
     public static string GetMySetting_ => AppServicesHelper.Config.Setting_1; 
     public static bool IsDev => AppServicesHelper.HostingEnvironment.IsDevelopment();
}

MyAppSettings.cs ist eine Klasse, die einem MyAppSettings-Abschnitt in appsettings.json zugeordnet ist:

public class MyAppSettings
{
    public string Setting_1 {get;set;}
}

appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "MyAppSettings": {
      "Setting_1": "something"
   }
 }
Soenhay
quelle
Vielen Dank an @Soenhay für das Teilen einer guten Lösung. Ich war auch mit dem gleichen Problem konfrontiert und habe das Problem nach Ihrem Ansatz gelöst.
Nadim Hossain Sonet
Ich benötige auch etwas Ähnliches, um die Repository-Referenz von der statischen Klasse zu übergeben. Kannst du etwas vorschlagen @Soenhay ???
Nadim Hossain Sonet
@NadimHossainSonet Es ist möglicherweise besser, es als separate Frage zu stellen, um eine vollständigere Antwort zu erhalten. Eine kurze Zusammenfassung lautet: Sie können dieselbe Technik wie oben verwenden. Erstellen Sie eine Serviceklasse und eine Schnittstelle für den Zugriff auf das Repository, registrieren Sie den Service in ConfigureServices und greifen Sie in der statischen Klasse über services.GetService darauf zu. Außerdem scheint es ziemlich selten zu sein, dass ich keinen DI-Ansatz finde, so dass Sie das vielleicht noch einmal überdenken.
Soenhay,
8

Ich stimme mcbowes zu, es steht in den Dokumenten , aber das erste Beispiel sieht eher so aus, wie Sie es brauchen ... wollen:

public class Program
{
    public static IConfigurationRoot Configuration { get; set; }
    public static void Main(string[] args = null)
    {
        var builder = new ConfigurationBuilder()
             .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json");

        Configuration = builder.Build();

        Console.WriteLine($"option1 = {Configuration["option1"]}");

        // Edit:
        IServiceCollection services = new ServiceCollection();
        services.AddOptions();
        services.Configure<HelloWorldOptions>(_configuration.GetSection("HelloWorld"));
        // And so on...
    }
}
Tubbe
quelle
3
Ja, aber wie können Sie Configurationvon einer anderen internen statischen Klasse auf diese Instanz zugreifen, ohne sie jedes Mal neu erstellen zu müssen?
Serj Sagan
Dann sollten Sie das OptionsMuster verwenden .
Tubbe
5
@Tubbe Könnten Sie ein Beispiel geben, wie das dann funktioniert? Nach dem, was ich in diesem Artikel gelesen habe, müssten Sie noch die Optionen im Konstruktor bereitstellen, was in einer statischen Klasse nicht möglich ist.
CGundlach
6

Vermeiden Sie die Verwendung einer statischen Klasse und verwenden Sie DI

namespace MyNamespace {

  public interface IConfig {
    string Username { get; }
    string Password { get; }
  }


  public class Config : IConfig {
    public Config(IConfiguration configuration) {
      _configuration = configuration;
    }
    readonly IConfiguration _configuration;
    public string Username => _configuration["Username"];
    public string Password => _configuration["Password"];
  }


}

Das Setup DI in der StartUp-Klasse

public class Startup {
  public void ConfigureServices(IServiceCollection services) {
    //...
    services.AddTransient<IConfig, Config>(); 
    ...
  }
}

Und benutze es so

  public class TestUsage {
    public TestUsage(IConfig config) {
      _config = config;
    }
    readonly IConfig _config;
    public string Username => _config.Username;
    public string Password => _config.Password;
  }
tb-mtg
quelle
2
Wenn Sie ein NET Framework auf NET Core migrieren, müssen Sie ALLE Klassen ändern, die Anwendungseinstellungen verwenden, um Konfigurationswerte (IOptions, IConfiguration oder was auch immer) einzufügen. Wenn Sie ein großes Projekt haben, nimmt das Bearbeiten von Klassen viel Zeit und Tests in Anspruch. : -O Würde gerne einen einfacheren Weg ohne DI sehen und Klassenkonstruktoren modifizieren
Junior Mayhé
3
Warum ist es so schlecht, eine statische Klasse zu haben? Ich weiß, dass der Zugriff auf Wörterbücher schnell ist, aber es ärgert mich nur ein wenig, wenn ich sie verwenden muss if(configuration["enabled"] == "True")oder if(configuration.GetValue<int>("enabled"))wenn ich das Gefühl habe, dass sie if(MyStaticClass.Enabled)für Methoden, die mehrmals pro Sekunde aufgerufen werden, schneller und leichter ist.
Dinerdo
^ Stimme voll und ganz zu. Dieser Ansatz (scrollen Sie nach unten zu "statische Instanz") wird immer noch von vielen Entwicklern bevorzugt: weblog.west-wind.com/posts/2017/dec/12/…
djsoteric
2

Sie können das Signleton- Muster verwenden, um von überall auf Ihre Konfigurationen zuzugreifen

    public class ConnectionStrings
    {
        private ConnectionStrings()
        {
        }
        // property with getter only will not work.
        public static ConnectionStrings Instance { get; protected set; } = new ConnectionStrings();

        public string DatabaseConnection { get; set; }
    }

und in Ihrer Startklasse

    public class Startup
    {
        private readonly IConfiguration configuration;

        public Startup(IConfiguration configuration)
        {
            this.configuration = configuration;
            configuration.GetSection("ConnectionStrings").Bind(ConnectionStrings.Instance);
        }

        public void ConfigureServices(IServiceCollection services)
        {
        }

        public void Configure(IApplicationBuilder app)
        {
        }
    }
Ahmed Aljaff
quelle
Sehr gutes Beispiel. Können Sie zeigen, wie Sie die Werte von appsettings.json abrufen und wie sie in SomeController.cs verwendet werden können
Software macht Spaß
Definieren Sie wie Verbindungszeichenfolgen eine andere Singleton-Klasse, nennen Sie sie AppSettings, binden Sie einen bestimmten Abschnitt an Ihre Singleton-Klasse und rufen Sie sie schließlich von überall auf. Beachten Sie jedoch, dass Änderungen in appsettings.json Ihre Singleton-Klasse erst nach einem Neustart der Anwendung beeinflussen. .
Ahmed Aljaff
Cool, danke - ich bevorzuge definitiv den gesamten Weg der Abhängigkeitsinjektion, aber während ich ein älteres Projekt schrittweise migriere, hilft mir dies, mich in diese Richtung zu bewegen!
dasch88
2

Dies wurde bereits gesagt, aber ich werde es sagen.

Ich glaube, .Net Core möchte, dass Entwickler Werte durch Dependency Inject erhalten. Dies ist mir aus meiner Forschung aufgefallen, aber ich spekuliere auch ein bisschen. Als Entwickler müssen wir diesem Paradigmenwechsel folgen, um .Net Core gut nutzen zu können.

Das Optionsmuster ist eine gute Alternative zur statischen Konfiguration. In Ihrem Fall sieht es so aus:

appsettings.json

{
  "Username": "MyUsername",
  "Password": "Password1234"
}

SystemUser.cs

public class SystemUser 
{
  public string Username { get; set; } = "";
  public string Password { get; set; } = "";
}

Startup.cs

services.Configure<SystemUser>(Configuration);

Um die SystemUser-Klasse zu verwenden, gehen wir wie folgt vor.

TestController.cs

public class TestController : Controller 
{
  private readonly SystemUser systemUser;

  public TestController(IOptionsMonitor<SystemUser> systemUserOptions)
  {
    this.systemUser = systemUserOptions.CurrentValue;
  }

  public void SomeMethod() 
  {
    var username = this.systemUser.Username; // "MyUsername"
    var password = this.systemUser.Password; // "Password1234"
  }
}

Obwohl wir keine statische Klasse verwenden, denke ich, dass dies die beste Alternative ist, die Ihren Anforderungen entspricht. Andernfalls müssen Sie möglicherweise eine statische Eigenschaft innerhalb der Startup-Klasse verwenden, was imo eine beängstigende Lösung darstellt.

christo8989
quelle
1
Statische Klassen sind keine bewährte Methode für die objektorientierte Programmierung . Sie meinen Stateful statische Klassen. Aber auch das ist ziemlich schwerfällig. DI in dieser Form ist wohl eher ein Konzept für das Komponentendesign als ein OOP-Konzept. Anrufer, die Kenntnisse über interne Abhängigkeiten haben müssen, sind nicht wirklich im Sinne des OOP-Ideals. Die Kompromissentwickler lieben es für granulare Unit-Tests. Viele von uns haben ein extrem anämisches OOP-Design, das näher an den imperativen Funktionen liegt, die um den Zustand herumgehen, als an OOP.
Joshua Enfield
Wie verwende ich das Optionsmuster, wenn ich von einer Bibliothek wie dem Nahverkehr abhängig bin und während der Konfiguration von Bus das Optionsmuster verwenden möchte, anstatt über Appsets auf fest codierte Zeichenfolgen zuzugreifen?
Kuldeep
0

Wenn Sie Umgebungsvariablen als Konfiguration verwenden , können Sie direkt auf die Umgebungsvariable und nicht über das Konfigurationsobjekt zugreifen.

using System;

namespace My.Example
{
    public static class GetPaths
    {
        private static readonly string MyPATH = 
            Environment.GetEnvironmentVariable("PATH");

        private static readonly string MySpecialPath =
            Environment.GetEnvironmentVariable("PREFIX_SpecialPath");
        ...
    }
}
Stephen
quelle
0

Persönlich mag ich die in diesem Link verwendete Methode

Im Wesentlichen wird Ihrer Optionsklasse lediglich ein statisches Feld hinzugefügt.

 public class WeblogConfiguration
 {
    public static WeblogConfiguration Current;

    public WeblogConfiguration()
    {
        Current = this;
    }
} 

Dann können Sie in jeder statischen Klasse Folgendes tun:

WeblogConfiguration.Current

Einfach und sehr einfach

TGN12
quelle
0

Ich denke, Sie könnten die Erweiterungsfunktion verwenden, so etwas

public static string ConfigToSomeThing(this IConfiguration config, int value)
        {
            return config[value.ToString()] ?? "";
        }

Dann an jedem Ort einfach IConfiguration injizieren und Erweiterungsmethode verwenden

_systemConfiguration.ConfigToSomeThing(123);
Grauer Wolf
quelle
0

Ich habe gerade unter Klasse erstellt:


    /// <summary>
    /// 
    /// </summary>
    public static class ConfigurationManager
    {
        /// <summary>
        /// 
        /// </summary>
        public sealed class ConfigurationManagerAppSettings
        {
            /// <summary>
            /// 
            /// </summary>
            internal ConfigurationManagerAppSettings() { }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="key"></param>
            /// <returns></returns>
            public string this[string key] => (TheConfiguration ?? throw new Exception("Set ConfigurationManager.TheConfiguration in Startup.cs")).GetSection($"AppSettings:{key}").Value;
        }

        /// <summary>
        /// 
        /// </summary>
        public static IConfiguration? TheConfiguration { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public static readonly ConfigurationManagerAppSettings AppSettings = new ConfigurationManagerAppSettings();
    }

und unter Code:

public class Startup
    {
        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) {
            ConfigurationManager.TheConfiguration = Configuration;

Homayoun Behzadian
quelle
-1

Hier finden Sie eine Möglichkeit, die Konfigurationswerte von einer NET.Core-Seite abzurufen, ohne diese statisch referenzieren zu müssen, sie aber dennoch an andere statische Funktionen übergeben zu können, die von der nicht statischen Klasse aufgerufen werden.

Fügen Sie oben in Ihrer nicht statischen Klasse Folgendes hinzu:

private readonly IConfiguration _configuration;

Bringen Sie dann in der Konstruktorfunktion die vorhandene Konfiguration als Eingabe in die Funktion ein: IConfiguration configuration

Weisen Sie dann die Konfiguration Ihrer schreibgeschützten Variablen innerhalb der Konstruktorfunktion zu: _configuration = configuration;

Hier ist ein Beispiel, wie es aussehen sollte:

public class IndexModel : PageModel
{
    private readonly IConfiguration _configuration;

    public IndexModel(IConfiguration configuration)
    {
        _configuration = configuration;
    }
}

Danach können Sie auf die Konfiguration in einer beliebigen Funktion in der Klasse verweisen, indem Sie auf _configuration verweisen, und diese dann auch an andere statische Funktionen weitergeben, die Sie von anderen Klassen aufrufen:

public async Task OnGetAsync()
{
    AnotherClass.SomeFunction(_configuration);
}

Dann kann ich in der aufgerufenen statischen Klasse die Konfigurationswerte verwenden:

public static string SomeFunction(IConfiguration configuration)
{
    string SomeValue = configuration.GetSection("SomeSectionOfConfig")["SomeValue"];
}

Ich habe eine Klasse, die einige gespeicherte Prozeduren zum Anzeigen und Ändern von Daten aufruft und Parameterwerte aus appsettings.json mit diesem Ansatz übergibt.

Robin Wilson
quelle
Die Frage ist, wie man "von einer statischen Klasse auf die Konfiguration zugreift", daher halte ich es nicht für sehr nützlich, uns hier zu sagen, wie man Konfigurationen erhält, "ohne diese statisch referenzieren zu müssen"
Colin
-4

Beachten Sie die Anweisungen unter Verwendung von hier für ASP.NET - Core - Konfiguration.

Sie können eine Klasse erstellen, um Ihre Konfigurationseinstellungen zu speichern und dann auf die Werte zuzugreifen.

_config.UserName

Im Start - ConfigureServices:

services.Configure<Config>(Configuration.GetSections("General"));

Dann injizieren Sie einfach Ihr Objekt, wo immer Sie es brauchen:

IOptions<Config> config
mcbowes
quelle
Wie werden Sie das Objekt in eine statische Klasse einfügen?
Mavi
Sie fügen die Konfiguration in eine Instanz ein, anstatt eine statische Klasse zu verwenden. Nirgendwo habe ich empfohlen, die Konfiguration in eine statische zu injizieren.
Mcbowes
3
Diese Frage bezieht sich speziell auf die Verwendung in einemstatic class
Serj Sagan
2
@SerjSagan Obwohl die Frage speziell nach statischen Klassen fragt, verstehen sie möglicherweise nicht, dass es einen Paradigmenwechsel von .NET zu .NET Core gab. Aus diesem Grund ist eine statische Klasse nicht die optimale Antwort und daher sollte das Optionsmuster eine gültige Antwort sein.
christo8989
-5

Die IConfiguration kann an einer beliebigen Stelle im Projekt injiziert werden. Aber im Fall der statischen Klasse die Option, die ich verwende und vielleicht nur annähern ... var Configuration = new ConfigurationBuilder() .AddUserSecrets<Startup>() .Build(); Und Sie können den erforderlichen Abschnitt hinzufügen, wie in diesem Codeblock oben, habe ich 'UserSecrets' hinzugefügt.

irejwanul
quelle
Sie können nicht in eine statische Aufladung eintauchen, daher ist dies die falsche Antwort.
Billb
Hallo billb, das habe ich oben erwähnt. Wir können nicht in eine statische Klasse DI, und deshalb der obige Codeblock. Wenn kein DI auftritt, aber der Zugriff auf die Konfiguration zulässig ist, z. B. 'UserSecrets' im Codeblock
irejwanul
Nun, Sie können nicht in eine statische Klasse DI, aber Sie können in eine Klasse DI, die statische private Mitglieder enthält. Das Hinzufügen zum Start-up-Service ist wahrscheinlich der beste Ansatz, aber es koppelt Ihre Projekte miteinander und erschwert die Wiederverwendung. Ich neige dazu, Statik in meine Serviceklassen aufzunehmen, wo sie vollständig gekapselt werden können.
iGanja