Optionsmuster in ASP.NET Core

Von Kirk Larkin und Rick Anderson

Das Optionsmuster verwendet Klassen, um stark typisierten Zugriff auf zusammengehörige Einstellungsgruppen zu ermöglichen. Wenn Konfigurationseinstellungen nach Szenario in separate Klassen isoliert werden, entspricht die Anwendung zwei wichtigen Prinzipien der Softwareentwicklung:

Optionen bieten auch einen Mechanismus, um Konfigurationsdaten zu validieren. Weitere Informationen finden Sie im Abschnitt Optionsvalidierung.

Dieses Thema enthält Informationen zu den Optionsmustern in ASP.NET Core. Informationen zur Verwendung des Optionsmusters in Konsolen-Apps finden Sie unter Optionsmuster in .NET.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)

Binden von hierarchischen Konfigurationsdaten

Die bevorzugte Methode für das Lesen zugehöriger Konfigurationswerte ist die Verwendung des Optionsmusters. Um z. B. die folgenden Konfigurationswerte zu lesen:

  "Position": {
    "Title": "Editor",
    "Name": "Joe Smith"
  }

Erstellen Sie die folgende neue PositionOptions-Klasse:

public class PositionOptions
{
    public const string Position = "Position";

    public string Title { get; set; }
    public string Name { get; set; }
}

Eine Optionsklasse:

  • Eine Optionsklasse muss nicht abstrakt sein und über einen öffentlichen parameterlosen Konstruktor verfügen.
  • Alle öffentlichen Lese-/Schreibeigenschaften des Typs sind gebunden.
  • Felder werden nicht gebunden. Im vorangehenden Code ist Position nicht gebunden. Die Position-Eigenschaft wird verwendet, sodass die Zeichenfolge "Position" nicht in der App hartcodiert werden muss, wenn die Klasse an einen Konfigurationsanbieter gebunden wird.

Der folgende Code

  • Ruft ConfigurationBinder.Bind auf, um die PositionOptions-Klasse an den Position-Abschnitt zu binden.
  • Zeigt die Position-Konfigurationsdaten an.
public class Test22Model : PageModel
{
    private readonly IConfiguration Configuration;

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

    public ContentResult OnGet()
    {
        var positionOptions = new PositionOptions();
        Configuration.GetSection(PositionOptions.Position).Bind(positionOptions);

        return Content($"Title: {positionOptions.Title} \n" +
                       $"Name: {positionOptions.Name}");
    }
}

Im vorangehenden Code werden standardmäßig Änderungen an der JSON-Konfigurationsdatei gelesen, nachdem die App gestartet wurde.

ConfigurationBinder.Get<T> bindet den angegebenen Typ und gibt ihn zurück. ConfigurationBinder.Get<T> ist möglicherweise praktischer als die Verwendung von ConfigurationBinder.Bind. Der folgende Code zeigt die Verwendung von ConfigurationBinder.Get<T> mit der PositionOptions-Klasse:

public class Test21Model : PageModel
{
    private readonly IConfiguration Configuration;
    public PositionOptions positionOptions { get; private set; }

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

    public ContentResult OnGet()
    {            
        positionOptions = Configuration.GetSection(PositionOptions.Position)
                                                     .Get<PositionOptions>();

        return Content($"Title: {positionOptions.Title} \n" +
                       $"Name: {positionOptions.Name}");
    }
}

Im vorangehenden Code werden standardmäßig Änderungen an der JSON-Konfigurationsdatei gelesen, nachdem die App gestartet wurde.

Eine alternative Vorgehensweise bei der Verwendung des Optionsmusters besteht darin, den Position-Abschnitt zu binden und ihn zum Dependency-Injection-Dienstcontainer hinzuzufügen. Im folgenden Code wird PositionOptions mit <xref:Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure_> zum Dienstcontainer hinzugefügt und an die Konfiguration gebunden:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(Configuration.GetSection(
                                        PositionOptions.Position));
    services.AddRazorPages();
}

Mithilfe des vorangehenden Codes liest der folgende Code die Positionsoptionen:

public class Test2Model : PageModel
{
    private readonly PositionOptions _options;

    public Test2Model(IOptions<PositionOptions> options)
    {
        _options = options.Value;
    }

    public ContentResult OnGet()
    {
        return Content($"Title: {_options.Title} \n" +
                       $"Name: {_options.Name}");
    }
}

Im vorangehenden Code werden Änderungen an der JSON-Konfigurationsdatei nach dem Start der App nicht gelesen. Verwenden Sie IOptionsSnapshot, um Änderungen lesen zu können, nachdem die App gestartet wurde.

Optionenschnittstellen

IOptions<TOptions>:

IOptionsSnapshot<TOptions>:

IOptionsMonitor<TOptions>:

Mit Postkonfigurationsszenarios können Sie Optionen festlegen oder ändern, nachdem alle IConfigureOptions<TOptions>-Konfigurationen durchgeführt wurden.

IOptionsFactory<TOptions> ist für das Erstellen neuer Optionsinstanzen zuständig. Es verfügt über eine einzelne Create-Methode. Die Standardimplementierung akzeptiert alle registrierten IConfigureOptions<TOptions> und IPostConfigureOptions<TOptions> und führt alle Konfigurationen zuerst und die Postkonfigurationen danach aus. Es wird zwischen IConfigureNamedOptions<TOptions> und IConfigureOptions<TOptions> unterschieden, und es werden nur die entsprechenden Schnittstellen aufgerufen.

IOptionsMonitorCache<TOptions> wird von IOptionsMonitor<TOptions> zum Zwischenspeichern der TOptions-Instanzen verwendet. IOptionsMonitorCache<TOptions> erklärt Optionsinstanzen im Monitor für ungültig, sodass der Wert neu berechnet wird (TryRemove). Werte können manuell mit TryAdd eingeführt werden. Die Clear-Methode wird verwendet, wenn alle benannten Instanzen bei Bedarf neu erstellt werden sollen.

Verwenden von IOptionsSnapshot zum Lesen aktualisierter Daten

Mithilfe von IOptionsSnapshot<TOptions> werden Optionen einmal pro Anforderung berechnet. Dies geschieht, wenn auf sie zugegriffen wird und sie für die Dauer der Anforderung zwischengespeichert werden. Änderungen an der Konfiguration werden gelesen, nachdem die App gestartet wurde, wenn Konfigurationsanbieter verwendet werden, die das Lesen aktualisierter Konfigurationswerte unterstützen.

Der Unterschied zwischen IOptionsMonitor und IOptionsSnapshot ist folgender:

  • IOptionsMonitor ist ein Singleton-Dienst, der zu jeder Zeit aktuelle Optionswerte empfängt, was insbesondere bei Singleton-Abhängigkeiten nützlich ist.
  • IOptionsSnapshot ist ein bereichsbezogener Dienst und bietet eine Momentaufnahme der Optionen zu dem Zeitpunkt, da das IOptionsSnapshot<T>-Objekt konstruiert wird. Momentaufnahmen von Optionen sind für die Verwendung mit vorübergehenden und bereichsbezogenen Abhängigkeiten bestimmt.

Der folgende Code verwendet IOptionsSnapshot<TOptions>.

public class TestSnapModel : PageModel
{
    private readonly MyOptions _snapshotOptions;

    public TestSnapModel(IOptionsSnapshot<MyOptions> snapshotOptionsAccessor)
    {
        _snapshotOptions = snapshotOptionsAccessor.Value;
    }

    public ContentResult OnGet()
    {
        return Content($"Option1: {_snapshotOptions.Option1} \n" +
                       $"Option2: {_snapshotOptions.Option2}");
    }
}

Der folgende Code registriert eine Konfigurationsinstanz, mit der MyOptions eine Bindung herstellt:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));

    services.AddRazorPages();
}

Im vorangehenden Code werden Änderungen an der JSON-Konfigurationsdatei nach dem Start der App gelesen.

IOptionsMonitor

Der folgende Code registriert eine Konfigurationsinstanz, mit der MyOptions eine Bindung herstellt.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));

    services.AddRazorPages();
}

Im folgenden Beispiel wird IOptionsMonitor<TOptions> verwendet:

public class TestMonitorModel : PageModel
{
    private readonly IOptionsMonitor<MyOptions> _optionsDelegate;

    public TestMonitorModel(IOptionsMonitor<MyOptions> optionsDelegate )
    {
        _optionsDelegate = optionsDelegate;
    }

    public ContentResult OnGet()
    {
        return Content($"Option1: {_optionsDelegate.CurrentValue.Option1} \n" +
                       $"Option2: {_optionsDelegate.CurrentValue.Option2}");
    }
}

Im vorangehenden Code werden standardmäßig Änderungen an der JSON-Konfigurationsdatei gelesen, nachdem die App gestartet wurde.

Unterstützung für benannte Optionen mit IConfigureNamedOptions

Benannte Optionen...

  • sind nützlich, wenn mehrere Konfigurationsabschnitte an die gleichen Eigenschaften gebunden werden.
  • unterscheiden zwischen Groß-/Kleinschreibung.

Betrachten Sie die folgende appsettings.json -Datei:

{
  "TopItem": {
    "Month": {
      "Name": "Green Widget",
      "Model": "GW46"
    },
    "Year": {
      "Name": "Orange Gadget",
      "Model": "OG35"
    }
  }
}

Anstatt zwei Klassen für die Bindung von TopItem:Month und TopItem:Year zu erstellen, wird folgende Klasse für jeden Abschnitt verwendet:

public class TopItemSettings
{
    public const string Month = "Month";
    public const string Year = "Year";

    public string Name { get; set; }
    public string Model { get; set; }
}

Der folgende Code dient zum Konfigurieren der benannten Optionen:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<TopItemSettings>(TopItemSettings.Month,
                                       Configuration.GetSection("TopItem:Month"));
    services.Configure<TopItemSettings>(TopItemSettings.Year,
                                        Configuration.GetSection("TopItem:Year"));

    services.AddRazorPages();
}

Der folgende Code zeigt die benannten Optionen an:

public class TestNOModel : PageModel
{
    private readonly TopItemSettings _monthTopItem;
    private readonly TopItemSettings _yearTopItem;

    public TestNOModel(IOptionsSnapshot<TopItemSettings> namedOptionsAccessor)
    {
        _monthTopItem = namedOptionsAccessor.Get(TopItemSettings.Month);
        _yearTopItem = namedOptionsAccessor.Get(TopItemSettings.Year);
    }

    public ContentResult OnGet()
    {
        return Content($"Month:Name {_monthTopItem.Name} \n" +
                       $"Month:Model {_monthTopItem.Model} \n\n" +
                       $"Year:Name {_yearTopItem.Name} \n" +
                       $"Year:Model {_yearTopItem.Model} \n"   );
    }
}

Alle Optionen sind benannte Instanzen. IConfigureOptions<TOptions>-Instanzen werden behandelt, als würden sie die Options.DefaultName-Instanz anzielen. Diese ist string.Empty. IConfigureNamedOptions<TOptions> implementiert auch IConfigureOptions<TOptions>. Die standardmäßige Implementierung von IOptionsFactory<TOptions> verfügt über eine Logik, um jede einzelne entsprechend zu verwenden. Die benannte Option null wird verwendet, um auf alle benannten Instanzen anstelle einer bestimmten benannten Instanz abzuzielen. ConfigureAll und PostConfigureAll verwenden diese Konvention.

OptionsBuilder-API

OptionsBuilder<TOptions> dient zum Konfigurieren von TOptions-Instanzen. OptionsBuilder optimiert die Erstellung von benannten Optionen, da es sich nur um einen einzelnen Parameter für den ersten AddOptions<TOptions>(string optionsName)-Aufruf handelt statt um alle nachfolgenden Aufrufe. Die Optionsvalidierung und die ConfigureOptions-Überladungen, die Dienstabhängigkeiten akzeptieren, sind nur über OptionsBuilder verfügbar.

OptionsBuilder wird im Abschnitt Überprüfung von Optionen verwendet.

Informationen zum Hinzufügen eines benutzerdefinierten Repositorys finden Sie unter Verwenden von AddOptions zum Konfigurieren eines benutzerdefinierten Repositorys.

Verwenden von DI-Diensten zum Konfigurieren von Optionen

Der Zugriff auf Dienste ist über die Dependency Injection möglich, während Optionen auf zwei Arten konfiguriert werden:

  • Übergeben Sie einen Konfigurationsdelegaten an Configure in OptionsBuilder<TOptions>. OptionsBuilder<TOptions> stellt Überladungen von Configure zur Verfügung, die es ermöglichen, bis zu fünf Dienste zu verwenden, um Optionen zu konfigurieren:

    services.AddOptions<MyOptions>("optionalName")
        .Configure<Service1, Service2, Service3, Service4, Service5>(
            (o, s, s2, s3, s4, s5) => 
                o.Property = DoSomethingWith(s, s2, s3, s4, s5));
    
  • Erstellen Sie einen Typ, der IConfigureOptions<TOptions> oder IConfigureNamedOptions<TOptions> implementiert, und registrieren Sie den Typ als Dienst.

Es empfiehlt sich das Übergeben eines Konfigurationsdelegaten an Konfigurieren, da das Erstellen eines Diensts etwas komplexer ist. Das Erstellen eines Typs entspricht den Aktionen des Frameworks beim Aufruf von Configure. Wenn Konfigurieren aufgerufen wird, wird eine vorübergehende, generische IConfigureNamedOptions<TOptions> registriert, die über einen Konstruktor verfügt, der die angegeben generischen Diensttypen akzeptiert.

Überprüfung von Optionen

Bei der Überprüfung der Optionen können Optionswerte überprüft werden.

Betrachten Sie die folgende appsettings.json -Datei:

{
  "MyConfig": {
    "Key1": "My Key One",
    "Key2": 10,
    "Key3": 32
  }
}

Die folgende Klasse erstellt eine Bindung zum "MyConfig"-Konfigurationsabschnitt und wendet einige DataAnnotations-Regeln an:

public class MyConfigOptions
{
    public const string MyConfig = "MyConfig";

    [RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")]
    public string Key1 { get; set; }
    [Range(0, 1000,
        ErrorMessage = "Value for {0} must be between {1} and {2}.")]
    public int Key2 { get; set; }
    public int Key3 { get; set; }
}

Der folgende Code

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

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOptions<MyConfigOptions>()
            .Bind(Configuration.GetSection(MyConfigOptions.MyConfig))
            .ValidateDataAnnotations();

        services.AddControllersWithViews();
    }

Die ValidateDataAnnotations-Erweiterungsmethode wird im NuGet-Paket Microsoft.Extensions.Options.DataAnnotations definiert. Für Web-Apps, die das Microsoft.NET.Sdk.Web SDK verwenden, wird auf dieses Paket implizit über das geteilte Framework verwiesen.

Der folgende Code zeigt die Konfigurationswerte oder die Überprüfungsfehler an:

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly IOptions<MyConfigOptions> _config;

    public HomeController(IOptions<MyConfigOptions> config,
                          ILogger<HomeController> logger)
    {
        _config = config;
        _logger = logger;

        try
        {
            var configValue = _config.Value;
           
        }
        catch (OptionsValidationException ex)
        {
            foreach (var failure in ex.Failures)
            {
                _logger.LogError(failure);
            }
        }
    }

    public ContentResult Index()
    {
        string msg;
        try
        {
             msg = $"Key1: {_config.Value.Key1} \n" +
                   $"Key2: {_config.Value.Key2} \n" +
                   $"Key3: {_config.Value.Key3}";
        }
        catch (OptionsValidationException optValEx)
        {
            return Content(optValEx.Message);
        }
        return Content(msg);
    }

Der folgende Code wendet mithilfe eines Delegaten eine komplexere Überprüfungsregel an:

public void ConfigureServices(IServiceCollection services)
{
    services.AddOptions<MyConfigOptions>()
        .Bind(Configuration.GetSection(MyConfigOptions.MyConfig))
        .ValidateDataAnnotations()
        .Validate(config =>
        {
            if (config.Key2 != 0)
            {
                return config.Key3 > config.Key2;
            }

            return true;
        }, "Key3 must be > than Key2.");   // Failure message.

    services.AddControllersWithViews();
}

IValidateOptions für die komplexe Überprüfung

Die folgende Klasse implementiert IValidateOptions<TOptions>:

public class MyConfigValidation : IValidateOptions<MyConfigOptions>
{
    public MyConfigOptions _config { get; private set; }

    public  MyConfigValidation(IConfiguration config)
    {
        _config = config.GetSection(MyConfigOptions.MyConfig)
            .Get<MyConfigOptions>();
    }

    public ValidateOptionsResult Validate(string name, MyConfigOptions options)
    {
        string vor=null;
        var rx = new Regex(@"^[a-zA-Z''-'\s]{1,40}$");
        var match = rx.Match(options.Key1);

        if (string.IsNullOrEmpty(match.Value))
        {
            vor = $"{options.Key1} doesn't match RegEx \n";
        }

        if ( options.Key2 < 0 || options.Key2 > 1000)
        {
            vor = $"{options.Key2} doesn't match Range 0 - 1000 \n";
        }

        if (_config.Key2 != default)
        {
            if(_config.Key3 <= _config.Key2)
            {
                vor +=  "Key3 must be > than Key2.";
            }
        }

        if (vor != null)
        {
            return ValidateOptionsResult.Fail(vor);
        }

        return ValidateOptionsResult.Success;
    }
}

IValidateOptions ermöglicht das Verschieben des Überprüfungscodes aus StartUp und in eine Klasse.

Mit dem vorangehenden Code wird die Überprüfung in Startup.ConfigureServices aktiviert:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<MyConfigOptions>(Configuration.GetSection(
                                        MyConfigOptions.MyConfig));
    services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions
                              <MyConfigOptions>, MyConfigValidation>());
    services.AddControllersWithViews();
}

Optionen für die Postkonfiguration

Legen Sie die Postkonfiguration mit IPostConfigureOptions<TOptions> fest. Die Postkonfiguration erfolgt, nachdem die gesamte IConfigureOptions<TOptions>-Konfiguration abgeschlossen ist:

services.PostConfigure<MyOptions>(myOptions =>
{
    myOptions.Option1 = "post_configured_option1_value";
});

PostConfigure ist nach dem Konfigurieren der benannten Optionen verfügbar:

services.PostConfigure<MyOptions>("named_options_1", myOptions =>
{
    myOptions.Option1 = "post_configured_option1_value";
});

Verwenden Sie PostConfigureAll zur Postkonfiguration aller Konfigurationsinstanzen:

services.PostConfigureAll<MyOptions>(myOptions =>
{
    myOptions.Option1 = "post_configured_option1_value";
});

Zugreifen auf Optionen während des Starts

IOptions<TOptions> und IOptionsMonitor<TOptions> können in Startup.Configure verwendet werden, da Dienste erstellt werden, bevor die Configure-Methode ausgeführt wird.

public void Configure(IApplicationBuilder app, 
    IOptionsMonitor<MyOptions> optionsAccessor)
{
    var option1 = optionsAccessor.CurrentValue.Option1;
}

Verwenden Sie IOptions<TOptions> oder IOptionsMonitor<TOptions> nicht in Startup.ConfigureServices. Es kann einen inkonsistenten Optionszustand geben. Dies liegt an der Reihenfolge der Dienstregistrierungen.

NuGet-Paket Options.ConfigurationExtensions

Auf das Paket Microsoft.Extensions.Options.ConfigurationExtensions wird implizit in ASP.NET Core-Apps verwiesen.

Das Optionsmuster verwendet Klassen, um Gruppen von zusammengehörigen Einstellungen darzustellen. Wenn Konfigurationseinstellungen nach Szenario in separate Klassen isoliert werden, entspricht die Anwendung zwei wichtigen Prinzipien der Softwareentwicklung:

Optionen bieten auch einen Mechanismus, um Konfigurationsdaten zu validieren. Weitere Informationen finden Sie im Abschnitt Optionsvalidierung.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)

Voraussetzungen

Verweisen Sie auf das Microsoft.AspNetCore.App-Metapaket, oder fügen Sie einen Paketverweis auf das Paket Microsoft.Extensions.Options.ConfigurationExtensions hinzu.

Optionenschnittstellen

IOptionsMonitor<TOptions> wird verwendet, um Optionen abzurufen und Benachrichtigungen über Optionen für TOptions-Instanzen zu verwalten. IOptionsMonitor<TOptions> unterstützt die folgenden Szenarios:

Mit Postkonfigurationsszenarien können Sie Optionen festlegen oder ändern, nachdem alle IConfigureOptions<TOptions>-Konfigurationen durchgeführt wurden.

IOptionsFactory<TOptions> ist für das Erstellen neuer Optionsinstanzen zuständig. Es verfügt über eine einzelne Create-Methode. Die Standardimplementierung akzeptiert alle registrierten IConfigureOptions<TOptions> und IPostConfigureOptions<TOptions> und führt alle Konfigurationen zuerst und die Postkonfigurationen danach aus. Es wird zwischen IConfigureNamedOptions<TOptions> und IConfigureOptions<TOptions> unterschieden, und es werden nur die entsprechenden Schnittstellen aufgerufen.

IOptionsMonitorCache<TOptions> wird von IOptionsMonitor<TOptions> zum Zwischenspeichern der TOptions-Instanzen verwendet. IOptionsMonitorCache<TOptions> erklärt Optionsinstanzen im Monitor für ungültig, sodass der Wert neu berechnet wird (TryRemove). Werte können manuell mit TryAdd eingeführt werden. Die Clear-Methode wird verwendet, wenn alle benannten Instanzen bei Bedarf neu erstellt werden sollen.

IOptionsSnapshot<TOptions> ist nützlich in Szenarien, in denen Optionen bei jeder Anforderung neu berechnet werden sollten. Weitere Informationen finden Sie im Abschnitt Neuladen der Konfigurationsdaten mit IOptionsSnapshot.

IOptions<TOptions> kann zum Unterstützen von Optionen verwendet werden. Allerdings unterstützt IOptions<TOptions> nicht die oben beschriebenen Szenarios von IOptionsMonitor<TOptions>. Sie können IOptions<TOptions> weiterhin in bestehenden Frameworks und Bibliotheken verwenden, die bereits die IOptions<TOptions>-Schnittstelle verwenden und nicht die von IOptionsMonitor<TOptions> bereitgestellten Szenarios benötigen.

Allgemeine Optionskonfiguration

Die allgemeine Optionskonfiguration wird als Beispiel 1 in der Beispiel-App veranschaulicht.

Eine Optionsklasse muss nicht abstrakt sein und über einen öffentlichen parameterlosen Konstruktor verfügen. Die folgende Klasse MyOptions verfügt über die zwei Eigenschaften: Option1 und Option2. Das Festlegen von Standardwerten ist optional, aber der Klassenkonstruktor im folgenden Beispiel legt den Standardwert von Option1 fest. Option2 hat den Standardwert festgelegt, indem die Eigenschaft direkt initialisiert wurde (Models/MyOptions.cs):

public class MyOptions
{
    public MyOptions()
    {
        // Set default value.
        Option1 = "value1_from_ctor";
    }
    
    public string Option1 { get; set; }
    public int Option2 { get; set; } = 5;
}

Die MyOptions-Klasse wird zum Dienstcontainer mit Configure hinzugefügt und an die Konfiguration gebunden:

// Example #1: General configuration
// Register the Configuration instance which MyOptions binds against.
services.Configure<MyOptions>(Configuration);

Das folgende Seitenmodel verwendet konstruktorbasierte Dependency Injection mit IOptionsMonitor<TOptions>, um auf die Einstellungen zugreifen zu können (Pages/Index.cshtml.cs):

private readonly MyOptions _options;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #1: Simple options
var option1 = _options.Option1;
var option2 = _options.Option2;
SimpleOptions = $"option1 = {option1}, option2 = {option2}";

Die appsettings.json -Datei des Beispiels gibt Werte für option1 und option2 an:

{
  "option1": "value1_from_json",
  "option2": -1,
  "subsection": {
    "suboption1": "subvalue1_from_json",
    "suboption2": 200
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Wenn die Anwendung ausgeführt wird, gibt die OnGet-Methode des Seitenmodells eine Zeichenfolge zurück, die die Werte der Optionsklasse anzeigt:

option1 = value1_from_json, option2 = -1

Hinweis

Wenn Sie eine benutzerdefinierte ConfigurationBuilder-Klasse verwenden, um Konfigurationsoptionen aus einer Einstellungsdatei zu laden, bestätigen Sie, dass der Basispfad ordnungsgemäß festgelegt ist:

var configBuilder = new ConfigurationBuilder()
   .SetBasePath(Directory.GetCurrentDirectory())
   .AddJsonFile("appsettings.json", optional: true);
var config = configBuilder.Build();

services.Configure<MyOptions>(config);

Sie müssen den Basispfad nicht explizit festlegen, wenn Sie Konfigurationsoptionen über CreateDefaultBuilder aus der Einstellungsdatei laden.

Konfigurieren von einfachen Optionen mit einem Delegaten

Das Konfigurieren von einfachen Optionen mit einem Delegaten wird als Beispiel 2 in der Beispiel-App veranschaulicht.

Verwenden Sie einen Delegaten zum Festlegen von Optionswerten. Die Beispielanwendung verwendet die MyOptionsWithDelegateConfig-Klasse (Models/MyOptionsWithDelegateConfig.cs):

public class MyOptionsWithDelegateConfig
{
    public MyOptionsWithDelegateConfig()
    {
        // Set default value.
        Option1 = "value1_from_ctor";
    }
    
    public string Option1 { get; set; }
    public int Option2 { get; set; } = 5;
}

Im folgenden Code wird ein zweiter IConfigureOptions<TOptions>-Dienst zum Dienstcontainer hinzugefügt. Er verwendet einen Delegaten zum Konfigurieren der Bindung mit MyOptionsWithDelegateConfig:

// Example #2: Options bound and configured by a delegate
services.Configure<MyOptionsWithDelegateConfig>(myOptions =>
{
    myOptions.Option1 = "value1_configured_by_delegate";
    myOptions.Option2 = 500;
});

Index.cshtml.cs:

private readonly MyOptionsWithDelegateConfig _optionsWithDelegateConfig;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #2: Options configured by delegate
var delegate_config_option1 = _optionsWithDelegateConfig.Option1;
var delegate_config_option2 = _optionsWithDelegateConfig.Option2;
SimpleOptionsWithDelegateConfig = 
    $"delegate_option1 = {delegate_config_option1}, " +
    $"delegate_option2 = {delegate_config_option2}";

Sie können mehrere Konfigurationsanbieter hinzufügen. Konfigurationsanbieter sind über NuGet-Pakete verfügbar und werden in der Reihenfolge ihrer Registrierung angewendet. Weitere Informationen finden Sie unter Konfiguration in ASP.NET Core.

Jeder Aufruf von Configure fügt einen IConfigureOptions<TOptions>-Dienst zum Dienstcontainer hinzu. Im vorherigen Beispiel wurden die Werte von Option1 und Option2 beide in appsettings.json angegeben, aber die Werte von Option1 und Option2 werden vom konfigurierten Delegaten überschrieben.

Wenn mehr als ein Konfigurationsdienst aktiviert ist, gewinnt die letzte angegebene Konfigurationsquelle und legt den Konfigurationswert fest. Wenn die Anwendung ausgeführt wird, gibt die OnGet-Methode des Seitenmodells eine Zeichenfolge zurück, die die Werte der Optionsklasse anzeigt:

delegate_option1 = value1_configured_by_delegate, delegate_option2 = 500

Konfigurieren von Unteroptionen

Das Konfigurieren von Unteroptionen wird als Beispiel 3 in der Beispiel-App veranschaulicht.

Anwendungen sollten Optionsklassen erstellen, die für bestimmte Szenariogruppen (Klassen) in der Anwendung gelten. Die Komponenten der Anwendung, die Konfigurationswerte erfordern, sollten nur über Zugriff auf die Konfigurationswerte verfügen, die sie verwenden.

Beim Binden von Optionen zur Konfiguration ist jede Eigenschaft im Optionstyp an einen Konfigurationsschlüssel des property[:sub-property:]-Formulars gebunden. Die MyOptions.Option1-Eigenschaft ist beispielsweise an den Schlüssel Option1 gebunden, der von der option1-Eigenschaft in appsettings.json gelesen wird.

Im folgenden Code wird ein dritter IConfigureOptions<TOptions>-Dienst zum Dienstcontainer hinzugefügt. Dadurch wird MySubOptions an den Abschnitt subsection der appsettings.json -Datei gebunden:

// Example #3: Suboptions
// Bind options using a sub-section of the appsettings.json file.
services.Configure<MySubOptions>(Configuration.GetSection("subsection"));

Die GetSection-Methode erfordert den Namespace Microsoft.Extensions.Configuration.

Die appsettings.json -Datei des Beispiels definiert einen subsection-Member mit Schlüsseln für suboption1 und suboption2:

{
  "option1": "value1_from_json",
  "option2": -1,
  "subsection": {
    "suboption1": "subvalue1_from_json",
    "suboption2": 200
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Die MySubOptions-Klasse definiert die Eigenschaften SubOption1 und SubOption2 zur Aufnahme der Optionswerte (Models/MySubOptions.cs):

public class MySubOptions
{
    public MySubOptions()
    {
        // Set default values.
        SubOption1 = "value1_from_ctor";
        SubOption2 = 5;
    }
    
    public string SubOption1 { get; set; }
    public int SubOption2 { get; set; }
}

Die OnGet-Methode des Seitenmodells gibt eine Zeichenfolge mit den Optionswerten (Pages/Index.cshtml.cs) zurück:

private readonly MySubOptions _subOptions;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #3: Suboptions
var subOption1 = _subOptions.SubOption1;
var subOption2 = _subOptions.SubOption2;
SubOptions = $"subOption1 = {subOption1}, subOption2 = {subOption2}";

Wenn die App ausgeführt wird, gibt die OnGet-Methode eine Zeichenfolge zurück, die die Werte der untergeordneten Optionsklasse anzeigt:

subOption1 = subvalue1_from_json, subOption2 = 200

Einfügung von Optionen

Die Einfügung von Optionen ist als Beispiel 4 in der Beispiel-App dargestellt.

Fügen Sie IOptionsMonitor<TOptions> ein in:

  • Eine Razor-Seite oder MVC-Ansicht mithilfe der @inject Razor-Anweisung.
  • Ein Seiten- oder Ansichtsmodell.

Im folgenden Beispiel aus der Beispiel-App wird IOptionsMonitor<TOptions> in ein Seitenmodell eingefügt (Pages/Index.cshtml.cs):

private readonly MyOptions _options;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #4: Bind options directly to the page
MyOptions = _options;

Die Beispiel-App zeigt das Einfügen von IOptionsMonitor<MyOptions> mit einer @inject-Anweisung:

@page
@model IndexModel
@using Microsoft.Extensions.Options
@inject IOptionsMonitor<MyOptions> OptionsAccessor
@{
    ViewData["Title"] = "Options Sample";
}

<h1>@ViewData["Title"]</h1>

Wenn die App ausgeführt wird, werden die Optionswerte in der gerenderten Seite angezeigt:

Optionswerte Option1: value1_from_json und Option2: -1 werden aus dem Modell geladen und in die Ansicht eingefügt.

Neuladen der Konfigurationsdaten mit IOptionsSnapshot

Das Neuladen der Konfigurationsdaten mit IOptionsSnapshot<TOptions> wird im Beispiel 5 in der Beispiel-App veranschaulicht.

Mithilfe von IOptionsSnapshot<TOptions> werden Optionen einmal pro Anforderung berechnet. Dies geschieht, wenn auf sie zugegriffen wird und sie für die Dauer der Anforderung zwischengespeichert werden.

Der Unterschied zwischen IOptionsMonitor und IOptionsSnapshot ist folgender:

  • IOptionsMonitor ist ein Singleton-Dienst, der zu jeder Zeit aktuelle Optionswerte empfängt, was insbesondere bei Singleton-Abhängigkeiten nützlich ist.
  • IOptionsSnapshot ist ein bereichsbezogener Dienst und bietet eine Momentaufnahme der Optionen zu dem Zeitpunkt, da das IOptionsSnapshot<T>-Objekt konstruiert wird. Momentaufnahmen von Optionen sind für die Verwendung mit vorübergehenden und bereichsbezogenen Abhängigkeiten bestimmt.

Im folgenden Beispiel wird veranschaulicht, wie eine neue IOptionsSnapshot<TOptions> nach den appsettings.json -Änderungen erstellt wird (Pages/Index.cshtml.cs). Mehrere Anforderungen an den Server geben konstante Werte zurück, die von der appsettings.json -Datei bereitgestellt werden, bis die Datei geändert wird und die Konfiguration neu lädt.

private readonly MyOptions _snapshotOptions;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #5: Snapshot options
var snapshotOption1 = _snapshotOptions.Option1;
var snapshotOption2 = _snapshotOptions.Option2;
SnapshotOptions = 
    $"snapshot option1 = {snapshotOption1}, " +
    $"snapshot option2 = {snapshotOption2}";

Die folgende Abbildung zeigt die anfänglichen option1- und option2-Werte, die aus der appsettings.json -Datei geladen werden:

snapshot option1 = value1_from_json, snapshot option2 = -1

Ändern Sie die Werte in der appsettings.json -Datei in value1_from_json UPDATED und 200. Speichern Sie die appsettings.json-Datei. Aktualisieren Sie den Browser, um festzustellen, ob die Optionswerte aktualisiert wurden:

snapshot option1 = value1_from_json UPDATED, snapshot option2 = 200

Unterstützung für benannte Optionen mit IConfigureNamedOptions

Unterstützung für benannte Optionen mit IConfigureNamedOptions<TOptions> wird als Beispiel 6 in der Beispiel-App veranschaulicht.

Die Unterstützung für benannte Optionen ermöglicht es der Anwendung, zwischen den Konfigurationen benannter Optionen zu unterscheiden. In der Beispiel-App werden benannte Optionen mit OptionsServiceCollectionExtensions.Configure deklariert. Dadurch wird wiederum die Erweiterungsmethode ConfigureNamedOptions<TOptions>.Configure aufgerufen. Benannte Optionen Unterscheidung nach Groß-/Kleinschreibung.

// Example #6: Named options (named_options_1)
// Register the ConfigurationBuilder instance which MyOptions binds against.
// Specify that the options loaded from configuration are named
// "named_options_1".
services.Configure<MyOptions>("named_options_1", Configuration);

// Example #6: Named options (named_options_2)
// Specify that the options loaded from the MyOptions class are named
// "named_options_2".
// Use a delegate to configure option values.
services.Configure<MyOptions>("named_options_2", myOptions =>
{
    myOptions.Option1 = "named_options_2_value1_from_action";
});

Die Beispielanwendung greift mit Get auf die benannten Optionen zu (Pages/Index.cshtml.cs):

private readonly MyOptions _named_options_1;
private readonly MyOptions _named_options_2;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #6: Named options
var named_options_1 = 
    $"named_options_1: option1 = {_named_options_1.Option1}, " +
    $"option2 = {_named_options_1.Option2}";
var named_options_2 = 
    $"named_options_2: option1 = {_named_options_2.Option1}, " +
    $"option2 = {_named_options_2.Option2}";
NamedOptions = $"{named_options_1} {named_options_2}";

Bei der Ausführung der Beispielanwendung werden die benannten Optionen zurückgegeben:

named_options_1: option1 = value1_from_json, option2 = -1
named_options_2: option1 = named_options_2_value1_from_action, option2 = 5

named_options_1-Werte werden von der Konfiguration bereitgestellt. Sie werden aus der appsettings.json -Datei geladen. named_options_2-Werte werden bereitgestellt vom:

  • named_options_2-Delegat in ConfigureServices für Option1.
  • Der Standardwert für Option2 wird von der MyOptions-Klasse bereitgestellt.

Konfigurieren aller Optionen mit der ConfigureAll-Methode

Konfigurieren Sie alle Optionsinstanzen mit der ConfigureAll-Methode. Der folgende Code konfiguriert Option1 für alle Konfigurationsinstanzen mit einem gemeinsamen Wert. Fügen Sie der Startup.ConfigureServices-Methode manuell den folgenden Code hinzu:

services.ConfigureAll<MyOptions>(myOptions => 
{
    myOptions.Option1 = "ConfigureAll replacement value";
});

Das Ausführen der Beispielanwendung nach dem Hinzufügen des Codes führt zu folgendem Ergebnis:

named_options_1: option1 = ConfigureAll replacement value, option2 = -1
named_options_2: option1 = ConfigureAll replacement value, option2 = 5

Hinweis

Alle Optionen sind benannte Instanzen. Vorhandene IConfigureOptions<TOptions>-Instanzen werden behandelt, als ob sie auf die Options.DefaultName-Instanz abzielen. Diese ist string.Empty. IConfigureNamedOptions<TOptions> implementiert auch IConfigureOptions<TOptions>. Die standardmäßige Implementierung von IOptionsFactory<TOptions> verfügt über eine Logik, um jede einzelne entsprechend zu verwenden. Die benannte Option null wird verwendet, um auf alle benannten Instanzen anstelle einer bestimmten benannten Instanz abzuzielen (ConfigureAll und PostConfigureAll verwenden diese Konvention).

OptionsBuilder-API

OptionsBuilder<TOptions> dient zum Konfigurieren von TOptions-Instanzen. OptionsBuilder optimiert die Erstellung von benannten Optionen, da es sich nur um einen einzelnen Parameter für den ersten AddOptions<TOptions>(string optionsName)-Aufruf handelt statt um alle nachfolgenden Aufrufe. Die Optionsvalidierung und die ConfigureOptions-Überladungen, die Dienstabhängigkeiten akzeptieren, sind nur über OptionsBuilder verfügbar.

// Options.DefaultName = "" is used.
services.AddOptions<MyOptions>().Configure(o => o.Property = "default");

services.AddOptions<MyOptions>("optionalName")
    .Configure(o => o.Property = "named");

Verwenden von DI-Diensten zum Konfigurieren von Optionen

Beim Konfigurieren von Optionen haben Sie zwei Möglichkeiten, auf andere Dienste aus Dependency Injections zuzugreifen:

Es empfiehlt sich das Übergeben eines Konfigurationsdelegaten an Konfigurieren, da das Erstellen eines Diensts etwas komplexer ist. Selbst einen eigenen Typ zu erstellen ist gleichbedeutend mit dem, was das Framework für Sie übernimmt, wenn Sie Konfigurieren verwenden. Wenn Konfigurieren aufgerufen wird, wird eine vorübergehende, generische IConfigureNamedOptions<TOptions> registriert, die über einen Konstruktor verfügt, der die angegeben generischen Diensttypen akzeptiert.

Überprüfung von Optionen

Sie können Optionen überprüfen, wenn diese konfiguriert werden. Rufen Sie Validate mit einer Überprüfungsmethode auf, die true zurückgibt, wenn die Optionen gültig sind, und false zurückgibt, wenn sie es nicht sind:

// Registration
services.AddOptions<MyOptions>("optionalOptionsName")
    .Configure(o => { }) // Configure the options
    .Validate(o => YourValidationShouldReturnTrueIfValid(o), 
        "custom error");

// Consumption
var monitor = services.BuildServiceProvider()
    .GetService<IOptionsMonitor<MyOptions>>();

try
{
    var options = monitor.Get("optionalOptionsName");
}
catch (OptionsValidationException e) 
{
   // e.OptionsName returns "optionalOptionsName"
   // e.OptionsType returns typeof(MyOptions)
   // e.Failures returns a list of errors, which would contain 
   //     "custom error"
}

Im vorherigen Beispiel wurde die benannte Optionsinstanz auf optionalOptionsName festgelegt. Die Standardoptionsinstanz ist Options.DefaultName.

Überprüfungen werden ausgeführt, wenn die Optionsinstanz erstellt wird. Eine Optionsinstanz besteht die Überprüfung beim ersten Zugriff garantiert.

Wichtig

Die Überprüfung von Optionen schützt nicht vor Änderungen an Optionen nach der Erstellung der Optionsinstanz. Beispielsweise werden IOptionsSnapshot-Optionen einmal pro Anforderung erstellt und überprüft, wenn zum ersten Mal auf die Optionen zugegriffen wird. Die IOptionsSnapshot-Optionen werden bei nachfolgenden Zugriffsversuchen für dieselbe Anforderung nicht erneut überprüft.

Die Validate-Methode akzeptiert ein Func<TOptions, bool>-Element. Um die Überprüfung vollständig anzupassen, implementieren Sie IValidateOptions<TOptions>. Dies ermöglicht Folgendes:

  • Eine Überprüfung von mehreren Optionstypen: class ValidateTwo : IValidateOptions<Option1>, IValidationOptions<Option2>
  • Eine Überprüfung, die von einem anderen Optionstyp abhängig ist: public DependsOnAnotherOptionValidator(IOptionsMonitor<AnotherOption> options)

IValidateOptions validiert Folgendes:

  • Eine bestimmte benannte Optionsinstanz.
  • Alle Optionen, wenn name den Wert null aufweist.

Geben Sie ein ValidateOptionsResult aus Ihrer Implementierung der Schnittstelle zurück:

public interface IValidateOptions<TOptions> where TOptions : class
{
    ValidateOptionsResult Validate(string name, TOptions options);
}

Die Überprüfung auf Basis von Datenanmerkungen ist über das Paket Microsoft.Extensions.Options.DataAnnotations durch Aufrufen der ValidateDataAnnotations-Methode in OptionsBuilder<TOptions> verfügbar. Microsoft.Extensions.Options.DataAnnotations ist im Microsoft.AspNetCore.App-Metapaket enthalten.

using Microsoft.Extensions.DependencyInjection;

private class AnnotatedOptions
{
    [Required]
    public string Required { get; set; }

    [StringLength(5, ErrorMessage = "Too long.")]
    public string StringLength { get; set; }

    [Range(-5, 5, ErrorMessage = "Out of range.")]
    public int IntRange { get; set; }
}

[Fact]
public void CanValidateDataAnnotations()
{
    var services = new ServiceCollection();
    services.AddOptions<AnnotatedOptions>()
        .Configure(o =>
        {
            o.StringLength = "111111";
            o.IntRange = 10;
            o.Custom = "nowhere";
        })
        .ValidateDataAnnotations();

    var sp = services.BuildServiceProvider();

    var error = Assert.Throws<OptionsValidationException>(() => 
        sp.GetRequiredService<IOptionsMonitor<AnnotatedOptions>>().CurrentValue);
    ValidateFailure<AnnotatedOptions>(error, Options.DefaultName, 1,
        "DataAnnotation validation failed for members Required " +
            "with the error 'The Required field is required.'.",
        "DataAnnotation validation failed for members StringLength " +
            "with the error 'Too long.'.",
        "DataAnnotation validation failed for members IntRange " +
            "with the error 'Out of range.'.");
}

Die vorzeitige Überprüfung (Fail-fast beim Start) wird für ein späteres Release in Erwägung.

Optionen für die Postkonfiguration

Legen Sie die Postkonfiguration mit IPostConfigureOptions<TOptions> fest. Die Postkonfiguration erfolgt, nachdem die gesamte IConfigureOptions<TOptions>-Konfiguration abgeschlossen ist:

services.PostConfigure<MyOptions>(myOptions =>
{
    myOptions.Option1 = "post_configured_option1_value";
});

PostConfigure ist nach dem Konfigurieren der benannten Optionen verfügbar:

services.PostConfigure<MyOptions>("named_options_1", myOptions =>
{
    myOptions.Option1 = "post_configured_option1_value";
});

Verwenden Sie PostConfigureAll zur Postkonfiguration aller Konfigurationsinstanzen:

services.PostConfigureAll<MyOptions>(myOptions =>
{
    myOptions.Option1 = "post_configured_option1_value";
});

Zugreifen auf Optionen während des Starts

IOptions<TOptions> und IOptionsMonitor<TOptions> können in Startup.Configure verwendet werden, da Dienste erstellt werden, bevor die Configure-Methode ausgeführt wird.

public void Configure(IApplicationBuilder app, IOptionsMonitor<MyOptions> optionsAccessor)
{
    var option1 = optionsAccessor.CurrentValue.Option1;
}

Verwenden Sie IOptions<TOptions> oder IOptionsMonitor<TOptions> nicht in Startup.ConfigureServices. Es kann einen inkonsistenten Optionszustand geben. Dies liegt an der Reihenfolge der Dienstregistrierungen.

Das Optionsmuster verwendet Klassen, um Gruppen von zusammengehörigen Einstellungen darzustellen. Wenn Konfigurationseinstellungen nach Szenario in separate Klassen isoliert werden, entspricht die Anwendung zwei wichtigen Prinzipien der Softwareentwicklung:

Optionen bieten auch einen Mechanismus, um Konfigurationsdaten zu validieren. Weitere Informationen finden Sie im Abschnitt Optionsvalidierung.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)

Voraussetzungen

Verweisen Sie auf das Microsoft.AspNetCore.App-Metapaket, oder fügen Sie einen Paketverweis auf das Paket Microsoft.Extensions.Options.ConfigurationExtensions hinzu.

Optionenschnittstellen

IOptionsMonitor<TOptions> wird verwendet, um Optionen abzurufen und Benachrichtigungen über Optionen für TOptions-Instanzen zu verwalten. IOptionsMonitor<TOptions> unterstützt die folgenden Szenarios:

Mit Postkonfigurationsszenarien können Sie Optionen festlegen oder ändern, nachdem alle IConfigureOptions<TOptions>-Konfigurationen durchgeführt wurden.

IOptionsFactory<TOptions> ist für das Erstellen neuer Optionsinstanzen zuständig. Es verfügt über eine einzelne Create-Methode. Die Standardimplementierung akzeptiert alle registrierten IConfigureOptions<TOptions> und IPostConfigureOptions<TOptions> und führt alle Konfigurationen zuerst und die Postkonfigurationen danach aus. Es wird zwischen IConfigureNamedOptions<TOptions> und IConfigureOptions<TOptions> unterschieden, und es werden nur die entsprechenden Schnittstellen aufgerufen.

IOptionsMonitorCache<TOptions> wird von IOptionsMonitor<TOptions> zum Zwischenspeichern der TOptions-Instanzen verwendet. IOptionsMonitorCache<TOptions> erklärt Optionsinstanzen im Monitor für ungültig, sodass der Wert neu berechnet wird (TryRemove). Werte können manuell mit TryAdd eingeführt werden. Die Clear-Methode wird verwendet, wenn alle benannten Instanzen bei Bedarf neu erstellt werden sollen.

IOptionsSnapshot<TOptions> ist nützlich in Szenarien, in denen Optionen bei jeder Anforderung neu berechnet werden sollten. Weitere Informationen finden Sie im Abschnitt Neuladen der Konfigurationsdaten mit IOptionsSnapshot.

IOptions<TOptions> kann zum Unterstützen von Optionen verwendet werden. Allerdings unterstützt IOptions<TOptions> nicht die oben beschriebenen Szenarios von IOptionsMonitor<TOptions>. Sie können IOptions<TOptions> weiterhin in bestehenden Frameworks und Bibliotheken verwenden, die bereits die IOptions<TOptions>-Schnittstelle verwenden und nicht die von IOptionsMonitor<TOptions> bereitgestellten Szenarios benötigen.

Allgemeine Optionskonfiguration

Die allgemeine Optionskonfiguration wird als Beispiel 1 in der Beispiel-App veranschaulicht.

Eine Optionsklasse muss nicht abstrakt sein und über einen öffentlichen parameterlosen Konstruktor verfügen. Die folgende Klasse MyOptions verfügt über die zwei Eigenschaften: Option1 und Option2. Das Festlegen von Standardwerten ist optional, aber der Klassenkonstruktor im folgenden Beispiel legt den Standardwert von Option1 fest. Option2 hat den Standardwert festgelegt, indem die Eigenschaft direkt initialisiert wurde (Models/MyOptions.cs):

public class MyOptions
{
    public MyOptions()
    {
        // Set default value.
        Option1 = "value1_from_ctor";
    }
    
    public string Option1 { get; set; }
    public int Option2 { get; set; } = 5;
}

Die MyOptions-Klasse wird zum Dienstcontainer mit Configure hinzugefügt und an die Konfiguration gebunden:

// Example #1: General configuration
// Register the Configuration instance which MyOptions binds against.
services.Configure<MyOptions>(Configuration);

Das folgende Seitenmodel verwendet konstruktorbasierte Dependency Injection mit IOptionsMonitor<TOptions>, um auf die Einstellungen zugreifen zu können (Pages/Index.cshtml.cs):

private readonly MyOptions _options;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #1: Simple options
var option1 = _options.Option1;
var option2 = _options.Option2;
SimpleOptions = $"option1 = {option1}, option2 = {option2}";

Die appsettings.json -Datei des Beispiels gibt Werte für option1 und option2 an:

{
  "option1": "value1_from_json",
  "option2": -1,
  "subsection": {
    "suboption1": "subvalue1_from_json",
    "suboption2": 200
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Wenn die Anwendung ausgeführt wird, gibt die OnGet-Methode des Seitenmodells eine Zeichenfolge zurück, die die Werte der Optionsklasse anzeigt:

option1 = value1_from_json, option2 = -1

Hinweis

Wenn Sie eine benutzerdefinierte ConfigurationBuilder-Klasse verwenden, um Konfigurationsoptionen aus einer Einstellungsdatei zu laden, bestätigen Sie, dass der Basispfad ordnungsgemäß festgelegt ist:

var configBuilder = new ConfigurationBuilder()
   .SetBasePath(Directory.GetCurrentDirectory())
   .AddJsonFile("appsettings.json", optional: true);
var config = configBuilder.Build();

services.Configure<MyOptions>(config);

Sie müssen den Basispfad nicht explizit festlegen, wenn Sie Konfigurationsoptionen über CreateDefaultBuilder aus der Einstellungsdatei laden.

Konfigurieren von einfachen Optionen mit einem Delegaten

Das Konfigurieren von einfachen Optionen mit einem Delegaten wird als Beispiel 2 in der Beispiel-App veranschaulicht.

Verwenden Sie einen Delegaten zum Festlegen von Optionswerten. Die Beispielanwendung verwendet die MyOptionsWithDelegateConfig-Klasse (Models/MyOptionsWithDelegateConfig.cs):

public class MyOptionsWithDelegateConfig
{
    public MyOptionsWithDelegateConfig()
    {
        // Set default value.
        Option1 = "value1_from_ctor";
    }
    
    public string Option1 { get; set; }
    public int Option2 { get; set; } = 5;
}

Im folgenden Code wird ein zweiter IConfigureOptions<TOptions>-Dienst zum Dienstcontainer hinzugefügt. Er verwendet einen Delegaten zum Konfigurieren der Bindung mit MyOptionsWithDelegateConfig:

// Example #2: Options bound and configured by a delegate
services.Configure<MyOptionsWithDelegateConfig>(myOptions =>
{
    myOptions.Option1 = "value1_configured_by_delegate";
    myOptions.Option2 = 500;
});

Index.cshtml.cs:

private readonly MyOptionsWithDelegateConfig _optionsWithDelegateConfig;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #2: Options configured by delegate
var delegate_config_option1 = _optionsWithDelegateConfig.Option1;
var delegate_config_option2 = _optionsWithDelegateConfig.Option2;
SimpleOptionsWithDelegateConfig = 
    $"delegate_option1 = {delegate_config_option1}, " +
    $"delegate_option2 = {delegate_config_option2}";

Sie können mehrere Konfigurationsanbieter hinzufügen. Konfigurationsanbieter sind über NuGet-Pakete verfügbar und werden in der Reihenfolge ihrer Registrierung angewendet. Weitere Informationen finden Sie unter Konfiguration in ASP.NET Core.

Jeder Aufruf von Configure fügt einen IConfigureOptions<TOptions>-Dienst zum Dienstcontainer hinzu. Im vorherigen Beispiel wurden die Werte von Option1 und Option2 beide in appsettings.json angegeben, aber die Werte von Option1 und Option2 werden vom konfigurierten Delegaten überschrieben.

Wenn mehr als ein Konfigurationsdienst aktiviert ist, gewinnt die letzte angegebene Konfigurationsquelle und legt den Konfigurationswert fest. Wenn die Anwendung ausgeführt wird, gibt die OnGet-Methode des Seitenmodells eine Zeichenfolge zurück, die die Werte der Optionsklasse anzeigt:

delegate_option1 = value1_configured_by_delegate, delegate_option2 = 500

Konfigurieren von Unteroptionen

Das Konfigurieren von Unteroptionen wird als Beispiel 3 in der Beispiel-App veranschaulicht.

Anwendungen sollten Optionsklassen erstellen, die für bestimmte Szenariogruppen (Klassen) in der Anwendung gelten. Die Komponenten der Anwendung, die Konfigurationswerte erfordern, sollten nur über Zugriff auf die Konfigurationswerte verfügen, die sie verwenden.

Beim Binden von Optionen zur Konfiguration ist jede Eigenschaft im Optionstyp an einen Konfigurationsschlüssel des property[:sub-property:]-Formulars gebunden. Die MyOptions.Option1-Eigenschaft ist beispielsweise an den Schlüssel Option1 gebunden, der von der option1-Eigenschaft in appsettings.json gelesen wird.

Im folgenden Code wird ein dritter IConfigureOptions<TOptions>-Dienst zum Dienstcontainer hinzugefügt. Dadurch wird MySubOptions an den Abschnitt subsection der appsettings.json -Datei gebunden:

// Example #3: Suboptions
// Bind options using a sub-section of the appsettings.json file.
services.Configure<MySubOptions>(Configuration.GetSection("subsection"));

Die GetSection-Methode erfordert den Namespace Microsoft.Extensions.Configuration.

Die appsettings.json -Datei des Beispiels definiert einen subsection-Member mit Schlüsseln für suboption1 und suboption2:

{
  "option1": "value1_from_json",
  "option2": -1,
  "subsection": {
    "suboption1": "subvalue1_from_json",
    "suboption2": 200
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Die MySubOptions-Klasse definiert die Eigenschaften SubOption1 und SubOption2 zur Aufnahme der Optionswerte (Models/MySubOptions.cs):

public class MySubOptions
{
    public MySubOptions()
    {
        // Set default values.
        SubOption1 = "value1_from_ctor";
        SubOption2 = 5;
    }
    
    public string SubOption1 { get; set; }
    public int SubOption2 { get; set; }
}

Die OnGet-Methode des Seitenmodells gibt eine Zeichenfolge mit den Optionswerten (Pages/Index.cshtml.cs) zurück:

private readonly MySubOptions _subOptions;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #3: Suboptions
var subOption1 = _subOptions.SubOption1;
var subOption2 = _subOptions.SubOption2;
SubOptions = $"subOption1 = {subOption1}, subOption2 = {subOption2}";

Wenn die App ausgeführt wird, gibt die OnGet-Methode eine Zeichenfolge zurück, die die Werte der untergeordneten Optionsklasse anzeigt:

subOption1 = subvalue1_from_json, subOption2 = 200

Optionen, die durch ein Ansichtsmodell oder mit direkter View Injection bereitgestellt werden

Optionen, die durch ein Ansichtsmodell oder mit direkter View Injection bereitgestellt werden, werden im Beispiel 4 in der Beispiel-App veranschaulicht.

Optionen können in einem Ansichtsmodell oder durch direkte Injection von IOptionsMonitor<TOptions> in eine Ansicht (Pages/Index.cshtml.cs) angegeben werden:

private readonly MyOptions _options;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #4: Bind options directly to the page
MyOptions = _options;

Die Beispiel-App zeigt das Einfügen von IOptionsMonitor<MyOptions> mit einer @inject-Anweisung:

@page
@model IndexModel
@using Microsoft.Extensions.Options
@inject IOptionsMonitor<MyOptions> OptionsAccessor
@{
    ViewData["Title"] = "Options Sample";
}

<h1>@ViewData["Title"]</h1>

Wenn die App ausgeführt wird, werden die Optionswerte in der gerenderten Seite angezeigt:

Optionswerte Option1: value1_from_json und Option2: -1 werden aus dem Modell geladen und in die Ansicht eingefügt.

Neuladen der Konfigurationsdaten mit IOptionsSnapshot

Das Neuladen der Konfigurationsdaten mit IOptionsSnapshot<TOptions> wird im Beispiel 5 in der Beispiel-App veranschaulicht.

IOptionsSnapshot<TOptions> unterstützt das Neuladen von Optionen mit minimalem Verarbeitungsaufwand.

Optionen werden einmal pro Anforderung berechnet. Dies geschieht, wenn auf sie zugegriffen wird und sie für die Dauer der Anforderung zwischengespeichert werden.

Im folgenden Beispiel wird veranschaulicht, wie eine neue IOptionsSnapshot<TOptions> nach den appsettings.json -Änderungen erstellt wird (Pages/Index.cshtml.cs). Mehrere Anforderungen an den Server geben konstante Werte zurück, die von der appsettings.json -Datei bereitgestellt werden, bis die Datei geändert wird und die Konfiguration neu lädt.

private readonly MyOptions _snapshotOptions;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #5: Snapshot options
var snapshotOption1 = _snapshotOptions.Option1;
var snapshotOption2 = _snapshotOptions.Option2;
SnapshotOptions = 
    $"snapshot option1 = {snapshotOption1}, " +
    $"snapshot option2 = {snapshotOption2}";

Die folgende Abbildung zeigt die anfänglichen option1- und option2-Werte, die aus der appsettings.json -Datei geladen werden:

snapshot option1 = value1_from_json, snapshot option2 = -1

Ändern Sie die Werte in der appsettings.json -Datei in value1_from_json UPDATED und 200. Speichern Sie die appsettings.json-Datei. Aktualisieren Sie den Browser, um festzustellen, ob die Optionswerte aktualisiert wurden:

snapshot option1 = value1_from_json UPDATED, snapshot option2 = 200

Unterstützung für benannte Optionen mit IConfigureNamedOptions

Unterstützung für benannte Optionen mit IConfigureNamedOptions<TOptions> wird als Beispiel 6 in der Beispiel-App veranschaulicht.

Die Unterstützung für benannte Optionen ermöglicht es der Anwendung, zwischen den Konfigurationen benannter Optionen zu unterscheiden. In der Beispiel-App werden benannte Optionen mit OptionsServiceCollectionExtensions.Configure deklariert. Dadurch wird wiederum die Erweiterungsmethode ConfigureNamedOptions<TOptions>.Configure aufgerufen. Benannte Optionen Unterscheidung nach Groß-/Kleinschreibung.

// Example #6: Named options (named_options_1)
// Register the ConfigurationBuilder instance which MyOptions binds against.
// Specify that the options loaded from configuration are named
// "named_options_1".
services.Configure<MyOptions>("named_options_1", Configuration);

// Example #6: Named options (named_options_2)
// Specify that the options loaded from the MyOptions class are named
// "named_options_2".
// Use a delegate to configure option values.
services.Configure<MyOptions>("named_options_2", myOptions =>
{
    myOptions.Option1 = "named_options_2_value1_from_action";
});

Die Beispielanwendung greift mit Get auf die benannten Optionen zu (Pages/Index.cshtml.cs):

private readonly MyOptions _named_options_1;
private readonly MyOptions _named_options_2;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #6: Named options
var named_options_1 = 
    $"named_options_1: option1 = {_named_options_1.Option1}, " +
    $"option2 = {_named_options_1.Option2}";
var named_options_2 = 
    $"named_options_2: option1 = {_named_options_2.Option1}, " +
    $"option2 = {_named_options_2.Option2}";
NamedOptions = $"{named_options_1} {named_options_2}";

Bei der Ausführung der Beispielanwendung werden die benannten Optionen zurückgegeben:

named_options_1: option1 = value1_from_json, option2 = -1
named_options_2: option1 = named_options_2_value1_from_action, option2 = 5

named_options_1-Werte werden von der Konfiguration bereitgestellt. Sie werden aus der appsettings.json -Datei geladen. named_options_2-Werte werden bereitgestellt vom:

  • named_options_2-Delegat in ConfigureServices für Option1.
  • Der Standardwert für Option2 wird von der MyOptions-Klasse bereitgestellt.

Konfigurieren aller Optionen mit der ConfigureAll-Methode

Konfigurieren Sie alle Optionsinstanzen mit der ConfigureAll-Methode. Der folgende Code konfiguriert Option1 für alle Konfigurationsinstanzen mit einem gemeinsamen Wert. Fügen Sie der Startup.ConfigureServices-Methode manuell den folgenden Code hinzu:

services.ConfigureAll<MyOptions>(myOptions => 
{
    myOptions.Option1 = "ConfigureAll replacement value";
});

Das Ausführen der Beispielanwendung nach dem Hinzufügen des Codes führt zu folgendem Ergebnis:

named_options_1: option1 = ConfigureAll replacement value, option2 = -1
named_options_2: option1 = ConfigureAll replacement value, option2 = 5

Hinweis

Alle Optionen sind benannte Instanzen. Vorhandene IConfigureOptions<TOptions>-Instanzen werden behandelt, als ob sie auf die Options.DefaultName-Instanz abzielen. Diese ist string.Empty. IConfigureNamedOptions<TOptions> implementiert auch IConfigureOptions<TOptions>. Die standardmäßige Implementierung von IOptionsFactory<TOptions> verfügt über eine Logik, um jede einzelne entsprechend zu verwenden. Die benannte Option null wird verwendet, um auf alle benannten Instanzen anstelle einer bestimmten benannten Instanz abzuzielen (ConfigureAll und PostConfigureAll verwenden diese Konvention).

OptionsBuilder-API

OptionsBuilder<TOptions> dient zum Konfigurieren von TOptions-Instanzen. OptionsBuilder optimiert die Erstellung von benannten Optionen, da es sich nur um einen einzelnen Parameter für den ersten AddOptions<TOptions>(string optionsName)-Aufruf handelt statt um alle nachfolgenden Aufrufe. Die Optionsvalidierung und die ConfigureOptions-Überladungen, die Dienstabhängigkeiten akzeptieren, sind nur über OptionsBuilder verfügbar.

// Options.DefaultName = "" is used.
services.AddOptions<MyOptions>().Configure(o => o.Property = "default");

services.AddOptions<MyOptions>("optionalName")
    .Configure(o => o.Property = "named");

Verwenden von DI-Diensten zum Konfigurieren von Optionen

Beim Konfigurieren von Optionen haben Sie zwei Möglichkeiten, auf andere Dienste aus Dependency Injections zuzugreifen:

Es empfiehlt sich das Übergeben eines Konfigurationsdelegaten an Konfigurieren, da das Erstellen eines Diensts etwas komplexer ist. Selbst einen eigenen Typ zu erstellen ist gleichbedeutend mit dem, was das Framework für Sie übernimmt, wenn Sie Konfigurieren verwenden. Wenn Konfigurieren aufgerufen wird, wird eine vorübergehende, generische IConfigureNamedOptions<TOptions> registriert, die über einen Konstruktor verfügt, der die angegeben generischen Diensttypen akzeptiert.

Optionen für die Postkonfiguration

Legen Sie die Postkonfiguration mit IPostConfigureOptions<TOptions> fest. Die Postkonfiguration erfolgt, nachdem die gesamte IConfigureOptions<TOptions>-Konfiguration abgeschlossen ist:

services.PostConfigure<MyOptions>(myOptions =>
{
    myOptions.Option1 = "post_configured_option1_value";
});

PostConfigure ist nach dem Konfigurieren der benannten Optionen verfügbar:

services.PostConfigure<MyOptions>("named_options_1", myOptions =>
{
    myOptions.Option1 = "post_configured_option1_value";
});

Verwenden Sie PostConfigureAll zur Postkonfiguration aller Konfigurationsinstanzen:

services.PostConfigureAll<MyOptions>(myOptions =>
{
    myOptions.Option1 = "post_configured_option1_value";
});

Zugreifen auf Optionen während des Starts

IOptions<TOptions> und IOptionsMonitor<TOptions> können in Startup.Configure verwendet werden, da Dienste erstellt werden, bevor die Configure-Methode ausgeführt wird.

public void Configure(IApplicationBuilder app, IOptionsMonitor<MyOptions> optionsAccessor)
{
    var option1 = optionsAccessor.CurrentValue.Option1;
}

Verwenden Sie IOptions<TOptions> oder IOptionsMonitor<TOptions> nicht in Startup.ConfigureServices. Es kann einen inkonsistenten Optionszustand geben. Dies liegt an der Reihenfolge der Dienstregistrierungen.

Zusätzliche Ressourcen