Sdílet prostřednictvím


Vzor možností v ASP.NET Core

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Upozorňující

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v tématu .NET a .NET Core Zásady podpory. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Rick Anderson.

Vzor možností používá třídy k zajištění přístupu silného typu ke skupinám souvisejících nastavení. Pokud jsou nastavení konfigurace izolované podle scénáře do samostatných tříd, aplikace dodržuje dva důležité principy softwarového inženýrství:

  • Zapouzdření:
    • Třídy, které závisí na nastavení konfigurace, závisí pouze na nastavení konfigurace, která používají.
  • Oddělení obav:
    • Nastavení pro různé části aplikace nejsou závislá ani vzájemně svázaná.

Možnosti také poskytují mechanismus pro ověření konfiguračních dat. Další informace najdete v části Možnosti ověření .

Tento článek obsahuje informace o vzoru možností v ASP.NET Core. Informace o použití vzoru možností v konzolových aplikacích najdete v tématu Možnosti v .NET.

Vytvoření vazby hierarchické konfigurace

Upřednostňovaným způsobem čtení souvisejících hodnot konfigurace je použití vzoru možností. Pokud chcete například přečíst následující konfigurační hodnoty:

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

Vytvořte následující třídu PositionOptions:

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

    public string Title { get; set; } = String.Empty;
    public string Name { get; set; } = String.Empty;
}

Třída možností:

  • Nesmí být abstraktní.
  • Má veřejné vlastnosti pro čtení i zápis typu, které mají odpovídající položky v konfiguraci jsou svázané.
  • Má vlastnosti pro čtení i zápis vázané na odpovídající položky v konfiguraci.
  • Neobsahuje svá pole svázaná. V předchozím kódu není pole Position vázáno. Pole Position se používá, aby řetězec "Position" nemusel být v aplikaci pevně zakódován při vytváření vazby třídy na zprostředkovatele konfigurace.

Následující kód:

  • Zavolá metodu ConfigurationBinder.Bind pro svázání třídy PositionOptions s oddílem Position.
  • Zobrazí konfigurační data pole Position.
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}");
    }
}

V předchozím kódu se ve výchozím nastavení změní konfigurační soubor JSON po spuštění aplikace.

ConfigurationBinder.Get<T> naváže a vrátí určený typ. ConfigurationBinder.Get<T> může být pohodlnější než použití ConfigurationBinder.Bind. Následující kód ukazuje, jak používat ConfigurationBinder.Get<T> s třídou PositionOptions:

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

V předchozím kódu se ve výchozím nastavení změní konfigurační soubor JSON po spuštění aplikace.

Bind také umožňuje zřetězení abstraktní třídy. Vezměte v úvahu následující kód, který používá abstraktní třídu SomethingWithAName:

namespace ConfigSample.Options;

public abstract class SomethingWithAName
{
    public abstract string? Name { get; set; }
}

public class NameTitleOptions(int age) : SomethingWithAName
{
    public const string NameTitle = "NameTitle";

    public override string? Name { get; set; }
    public string Title { get; set; } = string.Empty;

    public int Age { get; set; } = age;
}

Následující kód zobrazí NameTitleOptions konfigurační hodnoty:

public class Test33Model : PageModel
{
    private readonly IConfiguration Configuration;

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

    public ContentResult OnGet()
    {
        var nameTitleOptions = new NameTitleOptions(22);
        Configuration.GetSection(NameTitleOptions.NameTitle).Bind(nameTitleOptions);

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

Bind Volání jsou méně striktní než volání:Get<>

  • Bind umožňuje zřetězení abstraktního objektu.
  • Get<> aplikace musí vytvořit samotnou instanci.

Vzor Možností

Alternativním přístupem při používání vzoru možností je vytvoření vazby oddílu Position a jeho přidání do kontejneru služby vkládání závislostí. V následujícím kódu se PositionOptions přidá do kontejneru služby s příkazem Configure a sváže se s konfigurací:

using ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<PositionOptions>(
    builder.Configuration.GetSection(PositionOptions.Position));

var app = builder.Build();

Pomocí předchozího kódu přečte následující kód možnosti pozice:

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

V předchozím kódu se změny konfiguračního souboru JSON po spuštění aplikace nepřečtou. Pokud chcete po spuštění aplikace přečíst změny, použijte IOptionsSnapshot.

Rozhraní možností

IOptions<TOptions>:

IOptionsSnapshot<TOptions>:

IOptionsMonitor<TOptions>:

Scénáře po konfiguraci umožňují nastavení nebo změnu možností po dokončení konfigurace IConfigureOptions<TOptions> .

IOptionsFactory<TOptions> zodpovídá za vytváření nových instancí možností. Má jednu Create metodu. Výchozí implementace vezme všechny registrované IConfigureOptions<TOptions> a IPostConfigureOptions<TOptions> nejprve spustí všechny konfigurace, za kterými následuje po konfiguraci. Rozlišuje mezi IConfigureNamedOptions<TOptions> rozhraním a IConfigureOptions<TOptions> volá pouze příslušné rozhraní.

IOptionsMonitorCache<TOptions> používá IOptionsMonitor<TOptions> se k ukládání instancí do mezipaměti TOptions . Instance IOptionsMonitorCache<TOptions> možností v monitorování zneplatní, aby byla hodnota přepočítána (TryRemove). Hodnoty lze ručně zavést s TryAdd. Metoda Clear se používá, když se všechny pojmenované instance mají znovu vytvořit na vyžádání.

Čtení aktualizovaných dat pomocí IOptionsSnapshot

Pomocí IOptionsSnapshot<TOptions>:

  • Možnosti se po dobu životnosti požadavku počítají jednou za požadavek a ukládají se do mezipaměti.
  • Může dojít k významnému snížení výkonu, protože se jedná o službu s vymezeným oborem, která se přepočítá na každou žádost. Další informace najdete v tomto problému na GitHubu a vylepšete výkon vazby konfigurace.
  • Změny konfigurace se čtou po spuštění aplikace při použití zprostředkovatelů konfigurace, kteří podporují čtení aktualizovaných konfiguračních hodnot.

Rozdíl mezi IOptionsMonitor a IOptionsSnapshot je následující:

  • IOptionsMonitorje služba Singleton, která načítá aktuální hodnoty možností kdykoli, což je zvlášť užitečné v jednoúčelových závislostech.
  • IOptionsSnapshotje služba s vymezeným oborem a poskytuje snímek možností v době, IOptionsSnapshot<T> kdy je objekt vytvořen. Snímky možností jsou navržené pro použití s přechodnými a vymezenými závislostmi.

Následující kód používá 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}");
    }
}

Následující kód zaregistruje instanci konfigurace, která MyOptions se sváže s:

using SampleApp.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<MyOptions>(
    builder.Configuration.GetSection("MyOptions"));

var app = builder.Build();

V předchozím kódu se změny konfiguračního souboru JSON po spuštění aplikace načtou.

IOptionsMonitor

Následující kód zaregistruje instanci konfigurace, která MyOptions je svázaná s.

using SampleApp.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<MyOptions>(
    builder.Configuration.GetSection("MyOptions"));

var app = builder.Build();

Následující příklad používá IOptionsMonitor<TOptions>:

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

V předchozím kódu se ve výchozím nastavení změní konfigurační soubor JSON po spuštění aplikace.

Podpora pojmenovaných možností pomocí IConfigureNamedOptions

Pojmenované možnosti:

  • Jsou užitečné, když se několik oddílů konfigurace sváže se stejnými vlastnostmi.
  • Rozlišují se malá a velká písmena.

Vezměme například následující soubor appsettings.json:

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

Místo vytvoření dvou tříd pro vytvoření vazby TopItem:Month a TopItem:Yearpro každou část se používá následující třída:

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

    public string Name { get; set; } = string.Empty;
    public string Model { get; set; } = string.Empty;
}

Následující kód nakonfiguruje pojmenované možnosti:

using SampleApp.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
    builder.Configuration.GetSection("TopItem:Month"));
builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
    builder.Configuration.GetSection("TopItem:Year"));

var app = builder.Build();

Následující kód zobrazí pojmenované možnosti:

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

Všechny možnosti jsou pojmenované instance. IConfigureOptions<TOptions> instance jsou považovány za cíl instance Options.DefaultName , což je string.Empty. IConfigureNamedOptions<TOptions> implementuje IConfigureOptions<TOptions>také . Výchozí implementace má logiku IOptionsFactory<TOptions> , která se má použít odpovídajícím způsobem. Pojmenovaná null možnost se používá k cílení na všechny pojmenované instance místo na konkrétní pojmenovanou instanci. ConfigureAll a PostConfigureAll používat tuto konvenci.

OptionsBuilder API

OptionsBuilder<TOptions> slouží ke konfiguraci TOptions instancí. OptionsBuilder zjednodušuje vytváření pojmenovaných možností, protože se jedná pouze o jediný parametr počátečního AddOptions<TOptions>(string optionsName) volání místo toho, aby se zobrazoval ve všech následných voláních. Možnosti ověřování a ConfigureOptions přetížení, která přijímají závislosti služby, jsou k dispozici pouze prostřednictvím OptionsBuilder.

OptionsBuilder se používá v části Ověřování možností.

Informace o přidání vlastního úložiště najdete v tématu Použití addOptions ke konfiguraci vlastního úložiště .

Konfigurace možností pomocí služeb DI

Ke službám je možné přistupovat prostřednictvím injektáže závislostí a současně konfigurovat možnosti dvěma způsoby:

  • Předat delegáta konfigurace zapnuto Configure OptionsBuilder<TOptions>. OptionsBuilder<TOptions> poskytuje přetížení Configure , která umožňují použití až pěti služeb ke konfiguraci možností:

    builder.Services.AddOptions<MyOptions>("optionalName")
        .Configure<Service1, Service2, Service3, Service4, Service5>(
            (o, s, s2, s3, s4, s5) => 
                o.Property = DoSomethingWith(s, s2, s3, s4, s5));
    
  • Vytvořte typ, který implementuje IConfigureOptions<TOptions> nebo IConfigureNamedOptions<TOptions> zaregistruje typ jako službu.

Doporučujeme předat delegáta Configurekonfigurace , protože vytvoření služby je složitější. Vytvoření typu je ekvivalentní tomu, co architektura dělá při volání Configure. Volání Configure registruje přechodný obecný IConfigureNamedOptions<TOptions>, který má konstruktor, který přijímá obecné typy služby zadané.

Ověřování možností

Ověření možností umožňuje ověření hodnot možností.

Vezměme například následující soubor appsettings.json:

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

Následující třída se používá k vytvoření vazby k oddílu "MyConfig" konfigurace a používá několik DataAnnotations pravidel:

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

Následující kód:

using OptionsValidationSample.Configuration;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

builder.Services.AddOptions<MyConfigOptions>()
            .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
            .ValidateDataAnnotations();

var app = builder.Build();

Metoda ValidateDataAnnotations rozšíření je definována v balíčku NuGet Microsoft.Extensions.Options.DataAnnotations . U webových aplikací, které používají Microsoft.NET.Sdk.Web sadu SDK, se tento balíček implicitně odkazuje ze sdílené architektury.

Následující kód zobrazí konfigurační hodnoty nebo chyby ověření:

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

Následující kód používá složitější ověřovací pravidlo pomocí delegáta:

using OptionsValidationSample.Configuration;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

builder.Services.AddOptions<MyConfigOptions>()
            .Bind(builder.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.

var app = builder.Build();

IValidateOptions<TOptions> a IValidatableObject

Následující třída implementuje 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 umožňuje přesunout ověřovací kód mimo Program.cs třídu a do třídy.

Pomocí předchozího kódu je ověřování povoleno Program.cs pomocí následujícího kódu:

using Microsoft.Extensions.Options;
using OptionsValidationSample.Configuration;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

builder.Services.Configure<MyConfigOptions>(builder.Configuration.GetSection(
                                        MyConfigOptions.MyConfig));

builder.Services.AddSingleton<IValidateOptions
                              <MyConfigOptions>, MyConfigValidation>();

var app = builder.Build();

Ověřování možností také podporuje IValidatableObject. Ověření třídy v rámci samotné třídy:

ValidateOnStart

Při prvním TOption vytvoření instance se spustí ověření možností. To například znamená, že když dojde k prvnímu přístupu v IOptionsSnapshot<TOptions>.Value kanálu požadavku nebo když je volána v nastavení, která IOptionsMonitor<TOptions>.Get(string) jsou přítomna. Po opětovném načtení nastavení se ověření spustí znovu. Modul runtime ASP.NET Core používá OptionsCache<TOptions> k uložení instance možností do mezipaměti po jeho vytvoření.

Pokud chcete spouštět možnosti ověřování dychtivě, zavolejte ValidateOnStart<TOptions>(OptionsBuilder<TOptions>)Program.cspři spuštění aplikace:

builder.Services.AddOptions<MyConfigOptions>()
    .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
    .ValidateDataAnnotations()
    .ValidateOnStart();

Možnosti po konfiguraci

Nastavte po konfiguraci pomocí parametru IPostConfigureOptions<TOptions>. Po dokončení konfigurace se spustí po dokončení konfigurace IConfigureOptions<TOptions> :

using OptionsValidationSample.Configuration;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

builder.Services.AddOptions<MyConfigOptions>()
                .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));

builder.Services.PostConfigure<MyConfigOptions>(myOptions =>
{
    myOptions.Key1 = "post_configured_key1_value";
});

PostConfigure je k dispozici pro pokonfigurované pojmenované možnosti:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
    builder.Configuration.GetSection("TopItem:Month"));
builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
    builder.Configuration.GetSection("TopItem:Year"));

builder.Services.PostConfigure<TopItemSettings>("Month", myOptions =>
{
    myOptions.Name = "post_configured_name_value";
    myOptions.Model = "post_configured_model_value";
});

var app = builder.Build();

Slouží PostConfigureAll k následné konfiguraci všech instancí konfigurace:

using OptionsValidationSample.Configuration;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

builder.Services.AddOptions<MyConfigOptions>()
                .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));

builder.Services.PostConfigureAll<MyConfigOptions>(myOptions =>
{
    myOptions.Key1 = "post_configured_key1_value";
});

Možnosti aplikace Access v aplikaci Program.cs

Pokud chcete získat přístup nebo IOptions<TOptions> IOptionsMonitor<TOptions> v Program.csaplikaci, zavolejteGetRequiredService:WebApplication.Services

var app = builder.Build();

var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
    .CurrentValue.Option1;

Další materiály

Kirk Larkin a Rick Anderson.

Vzor možností používá třídy k zajištění přístupu silného typu ke skupinám souvisejících nastavení. Pokud jsou nastavení konfigurace izolované podle scénáře do samostatných tříd, aplikace dodržuje dva důležité principy softwarového inženýrství:

  • Zapouzdření:
    • Třídy, které závisí na nastavení konfigurace, závisí pouze na nastavení konfigurace, která používají.
  • Oddělení obav:
    • Nastavení pro různé části aplikace nejsou závislá ani vzájemně svázaná.

Možnosti také poskytují mechanismus pro ověření konfiguračních dat. Další informace najdete v části Možnosti ověření .

Tento článek obsahuje informace o vzoru možností v ASP.NET Core. Informace o použití vzoru možností v konzolových aplikacích najdete v tématu Možnosti v .NET.

Vytvoření vazby hierarchické konfigurace

Upřednostňovaným způsobem čtení souvisejících hodnot konfigurace je použití vzoru možností. Pokud chcete například přečíst následující konfigurační hodnoty:

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

Vytvořte následující třídu PositionOptions:

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

    public string Title { get; set; } = String.Empty;
    public string Name { get; set; } = String.Empty;
}

Třída možností:

  • Musí být neabstraktní s veřejným konstruktorem bez parametrů.
  • Všechny veřejné vlastnosti pro čtení i zápis typu jsou vázané.
  • Pole nejsou vázaná. V předchozím kódu není pole Position vázáno. Pole Position se používá, aby řetězec "Position" nemusel být v aplikaci pevně zakódován při vytváření vazby třídy na zprostředkovatele konfigurace.

Následující kód:

  • Zavolá metodu ConfigurationBinder.Bind pro svázání třídy PositionOptions s oddílem Position.
  • Zobrazí konfigurační data pole Position.
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}");
    }
}

V předchozím kódu se ve výchozím nastavení změní konfigurační soubor JSON po spuštění aplikace.

ConfigurationBinder.Get<T> naváže a vrátí určený typ. ConfigurationBinder.Get<T> může být pohodlnější než použití ConfigurationBinder.Bind. Následující kód ukazuje, jak používat ConfigurationBinder.Get<T> s třídou PositionOptions:

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

V předchozím kódu se ve výchozím nastavení změní konfigurační soubor JSON po spuštění aplikace.

Alternativním přístupem při používání vzoru možností je vytvoření vazby oddílu Position a jeho přidání do kontejneru služby vkládání závislostí. V následujícím kódu se PositionOptions přidá do kontejneru služby s příkazem Configure a sváže se s konfigurací:

using ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<PositionOptions>(
    builder.Configuration.GetSection(PositionOptions.Position));

var app = builder.Build();

Pomocí předchozího kódu přečte následující kód možnosti pozice:

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

V předchozím kódu se změny konfiguračního souboru JSON po spuštění aplikace nepřečtou. Pokud chcete po spuštění aplikace přečíst změny, použijte IOptionsSnapshot.

Rozhraní možností

IOptions<TOptions>:

IOptionsSnapshot<TOptions>:

IOptionsMonitor<TOptions>:

Scénáře po konfiguraci umožňují nastavení nebo změnu možností po dokončení konfigurace IConfigureOptions<TOptions> .

IOptionsFactory<TOptions> zodpovídá za vytváření nových instancí možností. Má jednu Create metodu. Výchozí implementace vezme všechny registrované IConfigureOptions<TOptions> a IPostConfigureOptions<TOptions> nejprve spustí všechny konfigurace, za kterými následuje po konfiguraci. Rozlišuje mezi IConfigureNamedOptions<TOptions> rozhraním a IConfigureOptions<TOptions> volá pouze příslušné rozhraní.

IOptionsMonitorCache<TOptions> používá IOptionsMonitor<TOptions> se k ukládání instancí do mezipaměti TOptions . Instance IOptionsMonitorCache<TOptions> možností v monitorování zneplatní, aby byla hodnota přepočítána (TryRemove). Hodnoty lze ručně zavést s TryAdd. Metoda Clear se používá, když se všechny pojmenované instance mají znovu vytvořit na vyžádání.

Čtení aktualizovaných dat pomocí IOptionsSnapshot

Pomocí IOptionsSnapshot<TOptions>:

  • Možnosti se po dobu životnosti požadavku počítají jednou za požadavek a ukládají se do mezipaměti.
  • Může dojít k významnému snížení výkonu, protože se jedná o službu s vymezeným oborem, která se přepočítá na každou žádost. Další informace najdete v tomto problému na GitHubu a vylepšete výkon vazby konfigurace.
  • Změny konfigurace se čtou po spuštění aplikace při použití zprostředkovatelů konfigurace, kteří podporují čtení aktualizovaných konfiguračních hodnot.

Rozdíl mezi IOptionsMonitor a IOptionsSnapshot je následující:

  • IOptionsMonitorje služba Singleton, která načítá aktuální hodnoty možností kdykoli, což je zvlášť užitečné v jednoúčelových závislostech.
  • IOptionsSnapshotje služba s vymezeným oborem a poskytuje snímek možností v době, IOptionsSnapshot<T> kdy je objekt vytvořen. Snímky možností jsou navržené pro použití s přechodnými a vymezenými závislostmi.

Následující kód používá 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}");
    }
}

Následující kód zaregistruje instanci konfigurace, která MyOptions se sváže s:

using SampleApp.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<MyOptions>(
    builder.Configuration.GetSection("MyOptions"));

var app = builder.Build();

V předchozím kódu se změny konfiguračního souboru JSON po spuštění aplikace načtou.

IOptionsMonitor

Následující kód zaregistruje instanci konfigurace, která MyOptions je svázaná s.

using SampleApp.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<MyOptions>(
    builder.Configuration.GetSection("MyOptions"));

var app = builder.Build();

Následující příklad používá IOptionsMonitor<TOptions>:

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

V předchozím kódu se ve výchozím nastavení změní konfigurační soubor JSON po spuštění aplikace.

Podpora pojmenovaných možností pomocí IConfigureNamedOptions

Pojmenované možnosti:

  • Jsou užitečné, když se několik oddílů konfigurace sváže se stejnými vlastnostmi.
  • Rozlišují se malá a velká písmena.

Vezměme například následující soubor appsettings.json:

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

Místo vytvoření dvou tříd pro vytvoření vazby TopItem:Month a TopItem:Yearpro každou část se používá následující třída:

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

    public string Name { get; set; } = string.Empty;
    public string Model { get; set; } = string.Empty;
}

Následující kód nakonfiguruje pojmenované možnosti:

using SampleApp.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
    builder.Configuration.GetSection("TopItem:Month"));
builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
    builder.Configuration.GetSection("TopItem:Year"));

var app = builder.Build();

Následující kód zobrazí pojmenované možnosti:

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

Všechny možnosti jsou pojmenované instance. IConfigureOptions<TOptions> instance jsou považovány za cíl instance Options.DefaultName , což je string.Empty. IConfigureNamedOptions<TOptions> implementuje IConfigureOptions<TOptions>také . Výchozí implementace má logiku IOptionsFactory<TOptions> , která se má použít odpovídajícím způsobem. Pojmenovaná null možnost se používá k cílení na všechny pojmenované instance místo na konkrétní pojmenovanou instanci. ConfigureAll a PostConfigureAll používat tuto konvenci.

OptionsBuilder API

OptionsBuilder<TOptions> slouží ke konfiguraci TOptions instancí. OptionsBuilder zjednodušuje vytváření pojmenovaných možností, protože se jedná pouze o jediný parametr počátečního AddOptions<TOptions>(string optionsName) volání místo toho, aby se zobrazoval ve všech následných voláních. Možnosti ověřování a ConfigureOptions přetížení, která přijímají závislosti služby, jsou k dispozici pouze prostřednictvím OptionsBuilder.

OptionsBuilder se používá v části Ověřování možností.

Informace o přidání vlastního úložiště najdete v tématu Použití addOptions ke konfiguraci vlastního úložiště .

Konfigurace možností pomocí služeb DI

Ke službám je možné přistupovat prostřednictvím injektáže závislostí a současně konfigurovat možnosti dvěma způsoby:

  • Předat delegáta konfigurace zapnuto Configure OptionsBuilder<TOptions>. OptionsBuilder<TOptions> poskytuje přetížení Configure , která umožňují použití až pěti služeb ke konfiguraci možností:

    builder.Services.AddOptions<MyOptions>("optionalName")
        .Configure<Service1, Service2, Service3, Service4, Service5>(
            (o, s, s2, s3, s4, s5) => 
                o.Property = DoSomethingWith(s, s2, s3, s4, s5));
    
  • Vytvořte typ, který implementuje IConfigureOptions<TOptions> nebo IConfigureNamedOptions<TOptions> zaregistruje typ jako službu.

Doporučujeme předat delegáta Configurekonfigurace , protože vytvoření služby je složitější. Vytvoření typu je ekvivalentní tomu, co architektura dělá při volání Configure. Volání Configure registruje přechodný obecný IConfigureNamedOptions<TOptions>, který má konstruktor, který přijímá obecné typy služby zadané.

Ověřování možností

Ověření možností umožňuje ověření hodnot možností.

Vezměme například následující soubor appsettings.json:

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

Následující třída se používá k vytvoření vazby k oddílu "MyConfig" konfigurace a používá několik DataAnnotations pravidel:

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

Následující kód:

using OptionsValidationSample.Configuration;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

builder.Services.AddOptions<MyConfigOptions>()
            .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
            .ValidateDataAnnotations();

var app = builder.Build();

Metoda ValidateDataAnnotations rozšíření je definována v balíčku NuGet Microsoft.Extensions.Options.DataAnnotations . U webových aplikací, které používají Microsoft.NET.Sdk.Web sadu SDK, se tento balíček implicitně odkazuje ze sdílené architektury.

Následující kód zobrazí konfigurační hodnoty nebo chyby ověření:

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

Následující kód používá složitější ověřovací pravidlo pomocí delegáta:

using OptionsValidationSample.Configuration;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

builder.Services.AddOptions<MyConfigOptions>()
            .Bind(builder.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.

var app = builder.Build();

IValidateOptions<TOptions> a IValidatableObject

Následující třída implementuje 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 umožňuje přesunout ověřovací kód mimo Program.cs třídu a do třídy.

Pomocí předchozího kódu je ověřování povoleno Program.cs pomocí následujícího kódu:

using Microsoft.Extensions.Options;
using OptionsValidationSample.Configuration;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

builder.Services.Configure<MyConfigOptions>(builder.Configuration.GetSection(
                                        MyConfigOptions.MyConfig));

builder.Services.AddSingleton<IValidateOptions
                              <MyConfigOptions>, MyConfigValidation>();

var app = builder.Build();

Ověřování možností také podporuje IValidatableObject. Ověření třídy v rámci samotné třídy:

ValidateOnStart

Při prvním vytvoření objektu IOptions<TOptions>, IOptionsSnapshot<TOptions>nebo IOptionsMonitor<TOptions> implementace se spustí ověření možností. Pokud chcete spouštět možnosti ověřování dychtivě, zavolejte ValidateOnStart Program.cspři spuštění aplikace:

builder.Services.AddOptions<MyConfigOptions>()
    .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
    .ValidateDataAnnotations()
    .ValidateOnStart();

Možnosti po konfiguraci

Nastavte po konfiguraci pomocí parametru IPostConfigureOptions<TOptions>. Po dokončení konfigurace se spustí po dokončení konfigurace IConfigureOptions<TOptions> :

using OptionsValidationSample.Configuration;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

builder.Services.AddOptions<MyConfigOptions>()
                .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));

builder.Services.PostConfigure<MyConfigOptions>(myOptions =>
{
    myOptions.Key1 = "post_configured_key1_value";
});

PostConfigure je k dispozici pro pokonfigurované pojmenované možnosti:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
    builder.Configuration.GetSection("TopItem:Month"));
builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
    builder.Configuration.GetSection("TopItem:Year"));

builder.Services.PostConfigure<TopItemSettings>("Month", myOptions =>
{
    myOptions.Name = "post_configured_name_value";
    myOptions.Model = "post_configured_model_value";
});

var app = builder.Build();

Slouží PostConfigureAll k následné konfiguraci všech instancí konfigurace:

using OptionsValidationSample.Configuration;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

builder.Services.AddOptions<MyConfigOptions>()
                .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));

builder.Services.PostConfigureAll<MyConfigOptions>(myOptions =>
{
    myOptions.Key1 = "post_configured_key1_value";
});

Možnosti aplikace Access v aplikaci Program.cs

Pokud chcete získat přístup nebo IOptions<TOptions> IOptionsMonitor<TOptions> v Program.csaplikaci, zavolejteGetRequiredService:WebApplication.Services

var app = builder.Build();

var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
    .CurrentValue.Option1;

Další materiály

Kirk Larkin a Rick Anderson.

Vzor možností používá třídy k zajištění přístupu silného typu ke skupinám souvisejících nastavení. Pokud jsou nastavení konfigurace izolované podle scénáře do samostatných tříd, aplikace dodržuje dva důležité principy softwarového inženýrství:

  • Zapouzdření:
    • Třídy, které závisí na nastavení konfigurace, závisí pouze na nastavení konfigurace, která používají.
  • Oddělení obav:
    • Nastavení pro různé části aplikace nejsou závislá ani vzájemně svázaná.

Možnosti také poskytují mechanismus pro ověření konfiguračních dat. Další informace najdete v části Možnosti ověření .

Toto téma obsahuje informace o vzoru možností v ASP.NET Core. Informace o použití vzoru možností v konzolových aplikacích najdete v tématu Možnosti v .NET.

Zobrazení nebo stažení ukázkového kódu (postup stažení)

Vytvoření vazby hierarchické konfigurace

Upřednostňovaným způsobem čtení souvisejících hodnot konfigurace je použití vzoru možností. Pokud chcete například přečíst následující konfigurační hodnoty:

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

Vytvořte následující třídu PositionOptions:

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

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

Třída možností:

  • Musí být neabstraktní s veřejným konstruktorem bez parametrů.
  • Všechny veřejné vlastnosti pro čtení i zápis typu jsou vázané.
  • Pole nejsou vázaná. V předchozím kódu není pole Position vázáno. Vlastnost Position se používá, aby řetězec "Position" nemusel být v aplikaci pevně zakódován při vytváření vazby třídy na zprostředkovatele konfigurace.

Následující kód:

  • Zavolá metodu ConfigurationBinder.Bind pro svázání třídy PositionOptions s oddílem Position.
  • Zobrazí konfigurační data pole Position.
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}");
    }
}

V předchozím kódu se ve výchozím nastavení změní konfigurační soubor JSON po spuštění aplikace.

ConfigurationBinder.Get<T> naváže a vrátí určený typ. ConfigurationBinder.Get<T> může být pohodlnější než použití ConfigurationBinder.Bind. Následující kód ukazuje, jak používat ConfigurationBinder.Get<T> s třídou PositionOptions:

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

V předchozím kódu se ve výchozím nastavení změní konfigurační soubor JSON po spuštění aplikace.

Alternativním přístupem při používání vzoru možností je vytvoření vazby oddílu Position a jeho přidání do kontejneru služby vkládání závislostí. V následujícím kódu se PositionOptions přidá do kontejneru služby s příkazem Configure a sváže se s konfigurací:

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

Pomocí předchozího kódu přečte následující kód možnosti pozice:

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

V předchozím kódu se změny konfiguračního souboru JSON po spuštění aplikace nepřečtou. Pokud chcete po spuštění aplikace přečíst změny, použijte IOptionsSnapshot.

Rozhraní možností

IOptions<TOptions>:

IOptionsSnapshot<TOptions>:

IOptionsMonitor<TOptions>:

Scénáře po konfiguraci umožňují nastavení nebo změnu možností po dokončení konfigurace IConfigureOptions<TOptions> .

IOptionsFactory<TOptions> zodpovídá za vytváření nových instancí možností. Má jednu Create metodu. Výchozí implementace vezme všechny registrované IConfigureOptions<TOptions> a IPostConfigureOptions<TOptions> nejprve spustí všechny konfigurace, za kterými následuje po konfiguraci. Rozlišuje mezi IConfigureNamedOptions<TOptions> rozhraním a IConfigureOptions<TOptions> volá pouze příslušné rozhraní.

IOptionsMonitorCache<TOptions> používá IOptionsMonitor<TOptions> se k ukládání instancí do mezipaměti TOptions . Instance IOptionsMonitorCache<TOptions> možností v monitorování zneplatní, aby byla hodnota přepočítána (TryRemove). Hodnoty lze ručně zavést s TryAdd. Metoda Clear se používá, když se všechny pojmenované instance mají znovu vytvořit na vyžádání.

Čtení aktualizovaných dat pomocí IOptionsSnapshot

Použití IOptionsSnapshot<TOptions>, možnosti se počítají jednou za požadavek při přístupu a ukládání do mezipaměti po dobu životnosti požadavku. Změny konfigurace se čtou po spuštění aplikace při použití zprostředkovatelů konfigurace, kteří podporují čtení aktualizovaných konfiguračních hodnot.

Rozdíl mezi IOptionsMonitor a IOptionsSnapshot je následující:

  • IOptionsMonitorje služba Singleton, která načítá aktuální hodnoty možností kdykoli, což je zvlášť užitečné v jednoúčelových závislostech.
  • IOptionsSnapshotje služba s vymezeným oborem a poskytuje snímek možností v době, IOptionsSnapshot<T> kdy je objekt vytvořen. Snímky možností jsou navržené pro použití s přechodnými a vymezenými závislostmi.

Následující kód používá 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}");
    }
}

Následující kód zaregistruje instanci konfigurace, která MyOptions se sváže s:

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

    services.AddRazorPages();
}

V předchozím kódu se změny konfiguračního souboru JSON po spuštění aplikace načtou.

IOptionsMonitor

Následující kód zaregistruje instanci konfigurace, která MyOptions je svázaná s.

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

    services.AddRazorPages();
}

Následující příklad používá IOptionsMonitor<TOptions>:

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

V předchozím kódu se ve výchozím nastavení změní konfigurační soubor JSON po spuštění aplikace.

Podpora pojmenovaných možností pomocí IConfigureNamedOptions

Pojmenované možnosti:

  • Jsou užitečné, když se několik oddílů konfigurace sváže se stejnými vlastnostmi.
  • Rozlišují se malá a velká písmena.

Vezměme například následující soubor appsettings.json:

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

Místo vytvoření dvou tříd pro vytvoření vazby TopItem:Month a TopItem:Yearpro každou část se používá následující třída:

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

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

Následující kód nakonfiguruje pojmenované možnosti:

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

Následující kód zobrazí pojmenované možnosti:

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

Všechny možnosti jsou pojmenované instance. IConfigureOptions<TOptions> instance jsou považovány za cíl instance Options.DefaultName , což je string.Empty. IConfigureNamedOptions<TOptions> implementuje IConfigureOptions<TOptions>také . Výchozí implementace má logiku IOptionsFactory<TOptions> , která se má použít odpovídajícím způsobem. Pojmenovaná null možnost se používá k cílení na všechny pojmenované instance místo na konkrétní pojmenovanou instanci. ConfigureAll a PostConfigureAll používat tuto konvenci.

OptionsBuilder API

OptionsBuilder<TOptions> slouží ke konfiguraci TOptions instancí. OptionsBuilder zjednodušuje vytváření pojmenovaných možností, protože se jedná pouze o jediný parametr počátečního AddOptions<TOptions>(string optionsName) volání místo toho, aby se zobrazoval ve všech následných voláních. Možnosti ověřování a ConfigureOptions přetížení, která přijímají závislosti služby, jsou k dispozici pouze prostřednictvím OptionsBuilder.

OptionsBuilder se používá v části Ověřování možností.

Informace o přidání vlastního úložiště najdete v tématu Použití addOptions ke konfiguraci vlastního úložiště .

Konfigurace možností pomocí služeb DI

Ke službám je možné přistupovat prostřednictvím injektáže závislostí a současně konfigurovat možnosti dvěma způsoby:

  • Předat delegáta konfigurace zapnuto Configure OptionsBuilder<TOptions>. OptionsBuilder<TOptions> poskytuje přetížení Configure , která umožňují použití až pěti služeb ke konfiguraci možností:

    services.AddOptions<MyOptions>("optionalName")
        .Configure<Service1, Service2, Service3, Service4, Service5>(
            (o, s, s2, s3, s4, s5) => 
                o.Property = DoSomethingWith(s, s2, s3, s4, s5));
    
  • Vytvořte typ, který implementuje IConfigureOptions<TOptions> nebo IConfigureNamedOptions<TOptions> zaregistruje typ jako službu.

Doporučujeme předat delegáta Configurekonfigurace , protože vytvoření služby je složitější. Vytvoření typu je ekvivalentní tomu, co architektura dělá při volání Configure. Volání Configure registruje přechodný obecný IConfigureNamedOptions<TOptions>, který má konstruktor, který přijímá obecné typy služby zadané.

Ověřování možností

Ověření možností umožňuje ověření hodnot možností.

Vezměme například následující soubor appsettings.json:

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

Následující třída vytvoří vazbu na "MyConfig" oddíl konfigurace a použije několik DataAnnotations pravidel:

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

Následující kód:

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

Metoda ValidateDataAnnotations rozšíření je definována v balíčku NuGet Microsoft.Extensions.Options.DataAnnotations . U webových aplikací, které používají Microsoft.NET.Sdk.Web sadu SDK, se tento balíček implicitně odkazuje ze sdílené architektury.

Následující kód zobrazí konfigurační hodnoty nebo chyby ověření:

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

Následující kód používá složitější ověřovací pravidlo pomocí delegáta:

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 pro komplexní ověřování

Následující třída implementuje 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 umožňuje přesunout ověřovací kód mimo StartUp třídu a do třídy.

Pomocí předchozího kódu je ověřování povoleno Startup.ConfigureServices pomocí následujícího kódu:

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

Možnosti po konfiguraci

Nastavte po konfiguraci pomocí parametru IPostConfigureOptions<TOptions>. Po dokončení konfigurace se spustí po dokončení konfigurace IConfigureOptions<TOptions> :

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

PostConfigure je k dispozici pro pokonfigurované pojmenované možnosti:

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

Slouží PostConfigureAll k následné konfiguraci všech instancí konfigurace:

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

Přístup k možnostem při spuštění

IOptions<TOptions> a IOptionsMonitor<TOptions> lze je použít v Startup.Configure, protože služby jsou vytvořeny před spuštěním Configure metody.

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

Nepoužívejte IOptions<TOptions> ani IOptionsMonitor<TOptions> v Startup.ConfigureServices. Kvůli objednávání registrací služeb může existovat nekonzistentní stav možností.

Options.ConfigurationExtensions – balíček NuGet

Na balíček Microsoft.Extensions.Options.ConfigurationExtensions se implicitně odkazuje v aplikacích ASP.NET Core.