Dela via


Alternativmönster i ASP.NET Core

Anmärkning

Det här är inte den senaste versionen av den här artikeln. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Varning

Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i supportpolicyn för .NET och .NET Core. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Viktigt!

Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.

För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Av Rick Anderson.

Alternativmönstret använder klasser för att ge starkt skrivskyddad åtkomst till grupper med relaterade inställningar. När konfigurationsinställningarna isoleras efter scenario i separata klasser följer appen två viktiga principer för programvaruteknik:

  • Inkapsling:
    • Klasser som är beroende av konfigurationsinställningar beror bara på de konfigurationsinställningar som de använder.
  • Separation av bekymmer:
    • Inställningar för olika delar av appen är inte beroende av eller kopplade till varandra.

Alternativen tillhandahåller också en mekanism för att verifiera konfigurationsdata. Mer information finns i avsnittet Alternativvalidering .

Den här artikeln innehåller information om alternativmönstret i ASP.NET Core. Information om hur du använder alternativmönstret i konsolappar finns i Alternativmönster i .NET.

Bind hierarkisk konfiguration

Det bästa sättet att läsa relaterade konfigurationsvärden är att använda mönstret alternativ. Om du till exempel vill läsa följande konfigurationsvärden:

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

Skapa följande PositionOptions klass:

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

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

En options-klass:

  • Måste vara icke-abstrakt.
  • Har offentliga läs- och skrivegenskaper av den typ som har motsvarande objekt i konfigurationen är bundna.
  • Har sina läs- och skrivegenskaper bundna till matchande poster i konfigurationen.
  • Har inte sina fält bundna. I föregående kod är inte Position bunden. Fältet Position används så att strängen "Position" inte behöver hårdkodas i appen när klassen binds till en konfigurationsprovider.

Följande kod:

  • Anropar ConfigurationBinder.Bind för att binda PositionOptions klassen till avsnittet Position .
  • Visar konfigurationsdata 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}");
    }
}

I föregående kod läses som standard ändringar i JSON-konfigurationsfilen när appen har startats.

ConfigurationBinder.Get<T> binder och returnerar den angivna typen. ConfigurationBinder.Get<T> kan vara bekvämare än att använda ConfigurationBinder.Bind. Följande kod visar hur du använder ConfigurationBinder.Get<T> med PositionOptions klassen:

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

I föregående kod läses som standard ändringar i JSON-konfigurationsfilen när appen har startats.

Bind tillåter också konkretion av en abstrakt klass. Överväg följande kod som använder den abstrakta klassen 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;
}

Följande kod visar konfigurationsvärdena NameTitleOptions :

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

Anrop till Bind är mindre strikta än anrop till Get<>:

  • Bind tillåter konkretion av ett abstrakt.
  • Get<> måste skapa en instans själv.

Mönstret för alternativ

En alternativ metod när du använder alternativmönstret är att binda Position avsnittet och lägga till det i containern för beroendeinmatningstjänsten. I följande kod PositionOptions läggs till i tjänstcontainern med Configure och är bunden till konfigurationen:

using ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

Med hjälp av föregående kod läser följande kod positionsalternativen:

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

I föregående kod läss inte ändringar i JSON-konfigurationsfilen när appen har startats. Om du vill läsa ändringar när appen har startats använder du IOptionsSnapshot.

Alternativgränssnitt

IOptions<TOptions>:

IOptionsSnapshot<TOptions>:

IOptionsMonitor<TOptions>:

Scenarier efter konfigurationen gör det möjligt att ställa in eller ändra alternativ när all IConfigureOptions<TOptions> konfiguration har inträffat.

IOptionsFactory<TOptions> ansvarar för att skapa nya alternativinstanser. Den har en enda Create metod. Standardimplementeringen tar alla registrerade IConfigureOptions<TOptions> och IPostConfigureOptions<TOptions> kör alla konfigurationer först, följt av efterkonfigurationen. Den skiljer mellan IConfigureNamedOptions<TOptions> och IConfigureOptions<TOptions> och anropar bara rätt gränssnitt.

IOptionsMonitorCache<TOptions> används av IOptionsMonitor<TOptions> för att cachelagrar TOptions instanser. Ogiltigförklarar IOptionsMonitorCache<TOptions> alternativinstanser i övervakaren så att värdet omberäknas (TryRemove). Värden kan introduceras manuellt med TryAdd. Metoden Clear används när alla namngivna instanser ska återskapas på begäran.

Använda IOptionsSnapshot för att läsa uppdaterade data

Att använda IOptionsSnapshot<TOptions>:

  • Alternativen beräknas en gång per begäran när de används och cachelagras under begärans livslängd.
  • Kan medföra en betydande prestandaförsämring eftersom det är en Scoped-tjänst och beräknas om per begäran. Mer information finns i det här GitHub-problemet och Förbättra prestanda för konfigurationsbindning.
  • Ändringar i konfigurationen läse när appen startar när du använder konfigurationsproviders som stöder läsning av uppdaterade konfigurationsvärden.

Skillnaden mellan IOptionsMonitor och IOptionsSnapshot är att:

  • IOptionsMonitor är en Singleton-tjänst som hämtar aktuella alternativvärden när som helst, vilket är särskilt användbart i singleton-beroenden.
  • IOptionsSnapshot är en Scoped-tjänst och ger en ögonblicksbild av alternativen när objektet IOptionsSnapshot<T> konstrueras. Ögonblicksbilder av alternativ är utformade för användning med tillfälliga och begränsade beroenden.

Följande kod använder 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}");
    }
}

Följande kod registrerar en konfigurationsinstans som MyOptions binder mot:

using SampleApp.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

I föregående kod läss ändringar i JSON-konfigurationsfilen när appen har startats.

IOptionsMonitor

Följande kod registrerar en konfigurationsinstans som MyOptions binder mot.

using SampleApp.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

I följande exempel används 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}");
    }
}

I föregående kod läses som standard ändringar i JSON-konfigurationsfilen när appen har startats.

Ange ett anpassat nyckelnamn för en konfigurationsegenskap med hjälp av ConfigurationKeyName

Som standard används egenskapsnamnen för options-klassen som nyckelnamn i konfigurationskällan. Om egenskapsnamnet är Titleär även nyckelnamnet i konfigurationen Title .

När namnen skiljer sig åt kan du använda attributetConfigurationKeyName för att ange nyckelnamnet i konfigurationskällan. Med den här tekniken kan du mappa en egenskap i konfigurationen till en i koden med ett annat namn.

Detta är användbart när egenskapsnamnet i konfigurationskällan inte är en giltig C#-identifierare eller när du vill använda ett annat namn i koden.

Tänk dig till exempel följande alternativklass:

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

    [ConfigurationKeyName("position-title")]
    public string Title { get; set; } = string.Empty;

    [ConfigurationKeyName("position-name")]
    public string Name { get; set; } = string.Empty;
}

Klassegenskaperna Title och Name är bundna till position-title och position-name från följande appsettings.json fil:

{
  "Position": {
    "position-title": "Editor",
    "position-name": "Joe Smith"
  }
}

Namngivna alternativ stöder användning av IConfigureNamedOptions

Namngivna alternativ:

  • Är användbara när flera konfigurationsavsnitt binder till samma egenskaper.
  • Är skiftlägeskänsliga.

Överväg följande appsettings.json fil:

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

I stället för att skapa två klasser för bindning TopItem:Month och TopItem:Yearanvänds följande klass för varje avsnitt:

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

Följande kod konfigurerar de namngivna alternativen:

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

Följande kod visar de namngivna alternativen:

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

Alla alternativ heter instanser. IConfigureOptions<TOptions> instanser behandlas som mål för instansen Options.DefaultName , som är string.Empty. IConfigureNamedOptions<TOptions> implementerar IConfigureOptions<TOptions>också . Standardimplementeringen av IOptionsFactory<TOptions> har logik för att använda var och en på rätt sätt. Det null namngivna alternativet används för att rikta alla namngivna instanser i stället för en specifik namngiven instans. ConfigureAll och PostConfigureAll använd den här konventionen.

OptionsBuilder API

OptionsBuilder<TOptions> används för att konfigurera TOptions instanser. OptionsBuilder effektiviserar skapandet av namngivna alternativ eftersom det bara är en enda parameter till det första AddOptions<TOptions>(string optionsName) anropet i stället för att visas i alla efterföljande anrop. Alternativvalidering och överlagringar ConfigureOptions som accepterar tjänstberoenden är endast tillgängliga via OptionsBuilder.

OptionsBuilder används i avsnittet Alternativvalidering .

Se Använda AddOptions för att konfigurera anpassad databas för information om hur du lägger till en anpassad databas.

Använda DI-tjänster för att konfigurera alternativ

Tjänster kan nås från beroendeinmatning när alternativen konfigureras på två sätt:

  • Skicka ett konfigurationsombud till ConfigureOptionsBuilder<TOptions>. OptionsBuilder<TOptions> Tillhandahåller överlagringar som Configure tillåter användning av upp till fem tjänster för att konfigurera alternativ:

    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));
    
  • Skapa en typ som implementerar IConfigureOptions<TOptions> eller IConfigureNamedOptions<TOptions> registrerar typen som en tjänst.

Vi rekommenderar att du skickar ett konfigurationsombud till , eftersom det är mer komplext att Configureskapa en tjänst. Att skapa en typ motsvarar vad ramverket gör när det anropar Configure. Anrop Configure registrerar en tillfällig generisk IConfigureNamedOptions<TOptions>, som har en konstruktor som accepterar de angivna allmänna tjänsttyperna.

Alternativvalidering

Alternativvalidering gör att alternativvärden kan verifieras.

Överväg följande appsettings.json fil:

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

Följande klass används för att binda till "MyConfig" konfigurationsavsnittet och tillämpar ett par DataAnnotations regler:

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

Följande kod:

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

Tilläggsmetoden ValidateDataAnnotations definieras i NuGet-paketet Microsoft.Extensions.Options.DataAnnotations . För webbappar som använder Microsoft.NET.Sdk.Web SDK refereras det här paketet implicit från det delade ramverket.

Följande kod visar konfigurationsvärdena eller valideringsfelen:

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

Följande kod tillämpar en mer komplex verifieringsregel med hjälp av ett ombud:

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> och IValidatableObject

Följande klass implementerar 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 Gör det möjligt att flytta valideringskoden från Program.cs och till en klass.

Med hjälp av föregående kod aktiveras validering med Program.cs följande kod:

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

Validering av alternativ stöder IValidatableObjectockså . Så här utför du validering på klassnivå av en klass i själva klassen:

ValidateOnStart

Alternativvalidering körs första gången en TOption instans skapas. Det innebär till exempel när den första åtkomsten sker IOptionsSnapshot<TOptions>.Value i en begärandepipeline eller när IOptionsMonitor<TOptions>.Get(string) den anropas på inställningar som finns. När inställningarna har lästs in igen körs verifieringen igen. Den ASP.NET Core-körningen används OptionsCache<TOptions> för att cachelagra alternativinstansen när den har skapats.

Om du vill köra alternativvalidering ivrigt när appen startar anropar du ValidateOnStart<TOptions>(OptionsBuilder<TOptions>):Program.cs

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

Alternativ efter konfiguration

Ange efterkonfiguration med IPostConfigureOptions<TOptions>. Efterkonfigurationen körs efter att all IConfigureOptions<TOptions> konfiguration har utförts:

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 är tillgängligt för efterkonfigurering av namngivna alternativ:

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

Använd PostConfigureAll för att efterkonfigurera alla konfigurationsinstanser:

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

Åtkomstalternativ i Program.cs

För att komma åt IOptions<TOptions> eller IOptionsMonitor<TOptions> i Program.cs, ring GetRequiredServiceWebApplication.Services:

var app = builder.Build();

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

Ytterligare resurser

Av Kirk Larkin och Rick Anderson.

Alternativmönstret använder klasser för att ge starkt skrivskyddad åtkomst till grupper med relaterade inställningar. När konfigurationsinställningarna isoleras efter scenario i separata klasser följer appen två viktiga principer för programvaruteknik:

  • Inkapsling:
    • Klasser som är beroende av konfigurationsinställningar beror bara på de konfigurationsinställningar som de använder.
  • Separation av bekymmer:
    • Inställningar för olika delar av appen är inte beroende av eller kopplade till varandra.

Alternativen tillhandahåller också en mekanism för att verifiera konfigurationsdata. Mer information finns i avsnittet Alternativvalidering .

Den här artikeln innehåller information om alternativmönstret i ASP.NET Core. Information om hur du använder alternativmönstret i konsolappar finns i Alternativmönster i .NET.

Bind hierarkisk konfiguration

Det bästa sättet att läsa relaterade konfigurationsvärden är att använda mönstret alternativ. Om du till exempel vill läsa följande konfigurationsvärden:

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

Skapa följande PositionOptions klass:

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

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

En options-klass:

  • Måste vara icke-abstrakt med en offentlig parameterlös konstruktor.
  • Alla offentliga läs- och skrivegenskaper av typen är bundna.
  • Fält är inte bundna. I föregående kod är inte Position bunden. Fältet Position används så att strängen "Position" inte behöver hårdkodas i appen när klassen binds till en konfigurationsprovider.

Följande kod:

  • Anropar ConfigurationBinder.Bind för att binda PositionOptions klassen till avsnittet Position .
  • Visar konfigurationsdata 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}");
    }
}

I föregående kod läses som standard ändringar i JSON-konfigurationsfilen när appen har startats.

ConfigurationBinder.Get<T> binder och returnerar den angivna typen. ConfigurationBinder.Get<T> kan vara bekvämare än att använda ConfigurationBinder.Bind. Följande kod visar hur du använder ConfigurationBinder.Get<T> med PositionOptions klassen:

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

I föregående kod läses som standard ändringar i JSON-konfigurationsfilen när appen har startats.

En alternativ metod när du använder alternativmönstret är att binda Position avsnittet och lägga till det i containern för beroendeinmatningstjänsten. I följande kod PositionOptions läggs till i tjänstcontainern med Configure och är bunden till konfigurationen:

using ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

Med hjälp av föregående kod läser följande kod positionsalternativen:

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

I föregående kod läss inte ändringar i JSON-konfigurationsfilen när appen har startats. Om du vill läsa ändringar när appen har startats använder du IOptionsSnapshot.

Alternativgränssnitt

IOptions<TOptions>:

IOptionsSnapshot<TOptions>:

IOptionsMonitor<TOptions>:

Scenarier efter konfigurationen gör det möjligt att ställa in eller ändra alternativ när all IConfigureOptions<TOptions> konfiguration har inträffat.

IOptionsFactory<TOptions> ansvarar för att skapa nya alternativinstanser. Den har en enda Create metod. Standardimplementeringen tar alla registrerade IConfigureOptions<TOptions> och IPostConfigureOptions<TOptions> kör alla konfigurationer först, följt av efterkonfigurationen. Den skiljer mellan IConfigureNamedOptions<TOptions> och IConfigureOptions<TOptions> och anropar bara rätt gränssnitt.

IOptionsMonitorCache<TOptions> används av IOptionsMonitor<TOptions> för att cachelagrar TOptions instanser. Ogiltigförklarar IOptionsMonitorCache<TOptions> alternativinstanser i övervakaren så att värdet omberäknas (TryRemove). Värden kan introduceras manuellt med TryAdd. Metoden Clear används när alla namngivna instanser ska återskapas på begäran.

Använda IOptionsSnapshot för att läsa uppdaterade data

Att använda IOptionsSnapshot<TOptions>:

  • Alternativen beräknas en gång per begäran när de används och cachelagras under begärans livslängd.
  • Kan medföra en betydande prestandaförsämring eftersom det är en Scoped-tjänst och beräknas om per begäran. Mer information finns i det här GitHub-problemet och Förbättra prestanda för konfigurationsbindning.
  • Ändringar i konfigurationen läse när appen startar när du använder konfigurationsproviders som stöder läsning av uppdaterade konfigurationsvärden.

Skillnaden mellan IOptionsMonitor och IOptionsSnapshot är att:

  • IOptionsMonitor är en Singleton-tjänst som hämtar aktuella alternativvärden när som helst, vilket är särskilt användbart i singleton-beroenden.
  • IOptionsSnapshot är en Scoped-tjänst och ger en ögonblicksbild av alternativen när objektet IOptionsSnapshot<T> konstrueras. Ögonblicksbilder av alternativ är utformade för användning med tillfälliga och begränsade beroenden.

Följande kod använder 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}");
    }
}

Följande kod registrerar en konfigurationsinstans som MyOptions binder mot:

using SampleApp.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

I föregående kod läss ändringar i JSON-konfigurationsfilen när appen har startats.

IOptionsMonitor

Följande kod registrerar en konfigurationsinstans som MyOptions binder mot.

using SampleApp.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

I följande exempel används 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}");
    }
}

I föregående kod läses som standard ändringar i JSON-konfigurationsfilen när appen har startats.

Namngivna alternativ stöder användning av IConfigureNamedOptions

Namngivna alternativ:

  • Är användbara när flera konfigurationsavsnitt binder till samma egenskaper.
  • Är skiftlägeskänsliga.

Överväg följande appsettings.json fil:

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

I stället för att skapa två klasser för bindning TopItem:Month och TopItem:Yearanvänds följande klass för varje avsnitt:

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

Följande kod konfigurerar de namngivna alternativen:

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

Följande kod visar de namngivna alternativen:

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

Alla alternativ heter instanser. IConfigureOptions<TOptions> instanser behandlas som mål för instansen Options.DefaultName , som är string.Empty. IConfigureNamedOptions<TOptions> implementerar IConfigureOptions<TOptions>också . Standardimplementeringen av IOptionsFactory<TOptions> har logik för att använda var och en på rätt sätt. Det null namngivna alternativet används för att rikta alla namngivna instanser i stället för en specifik namngiven instans. ConfigureAll och PostConfigureAll använd den här konventionen.

OptionsBuilder API

OptionsBuilder<TOptions> används för att konfigurera TOptions instanser. OptionsBuilder effektiviserar skapandet av namngivna alternativ eftersom det bara är en enda parameter till det första AddOptions<TOptions>(string optionsName) anropet i stället för att visas i alla efterföljande anrop. Alternativvalidering och överlagringar ConfigureOptions som accepterar tjänstberoenden är endast tillgängliga via OptionsBuilder.

OptionsBuilder används i avsnittet Alternativvalidering .

Se Använda AddOptions för att konfigurera anpassad databas för information om hur du lägger till en anpassad databas.

Använda DI-tjänster för att konfigurera alternativ

Tjänster kan nås från beroendeinmatning när alternativen konfigureras på två sätt:

  • Skicka ett konfigurationsombud till ConfigureOptionsBuilder<TOptions>. OptionsBuilder<TOptions> Tillhandahåller överlagringar som Configure tillåter användning av upp till fem tjänster för att konfigurera alternativ:

    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));
    
  • Skapa en typ som implementerar IConfigureOptions<TOptions> eller IConfigureNamedOptions<TOptions> registrerar typen som en tjänst.

Vi rekommenderar att du skickar ett konfigurationsombud till , eftersom det är mer komplext att Configureskapa en tjänst. Att skapa en typ motsvarar vad ramverket gör när det anropar Configure. Anrop Configure registrerar en tillfällig generisk IConfigureNamedOptions<TOptions>, som har en konstruktor som accepterar de angivna allmänna tjänsttyperna.

Alternativvalidering

Alternativvalidering gör att alternativvärden kan verifieras.

Överväg följande appsettings.json fil:

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

Följande klass används för att binda till "MyConfig" konfigurationsavsnittet och tillämpar ett par DataAnnotations regler:

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

Följande kod:

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

Tilläggsmetoden ValidateDataAnnotations definieras i NuGet-paketet Microsoft.Extensions.Options.DataAnnotations . För webbappar som använder Microsoft.NET.Sdk.Web SDK refereras det här paketet implicit från det delade ramverket.

Följande kod visar konfigurationsvärdena eller valideringsfelen:

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

Följande kod tillämpar en mer komplex verifieringsregel med hjälp av ett ombud:

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> och IValidatableObject

Följande klass implementerar 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 Gör det möjligt att flytta valideringskoden från Program.cs och till en klass.

Med hjälp av föregående kod aktiveras validering med Program.cs följande kod:

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

Validering av alternativ stöder IValidatableObjectockså . Så här utför du validering på klassnivå av en klass i själva klassen:

ValidateOnStart

Alternativvalidering körs första gången en IOptions<TOptions>, IOptionsSnapshot<TOptions>eller IOptionsMonitor<TOptions> implementering skapas. Om du vill köra alternativvalidering ivrigt när appen startar anropar du ValidateOnStart :Program.cs

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

Alternativ efter konfiguration

Ange efterkonfiguration med IPostConfigureOptions<TOptions>. Efterkonfigurationen körs efter att all IConfigureOptions<TOptions> konfiguration har utförts:

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 är tillgängligt för efterkonfigurering av namngivna alternativ:

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

Använd PostConfigureAll för att efterkonfigurera alla konfigurationsinstanser:

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

Åtkomstalternativ i Program.cs

För att komma åt IOptions<TOptions> eller IOptionsMonitor<TOptions> i Program.cs, ring GetRequiredServiceWebApplication.Services:

var app = builder.Build();

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

Ytterligare resurser

Av Kirk Larkin och Rick Anderson.

Alternativmönstret använder klasser för att ge starkt skrivskyddad åtkomst till grupper med relaterade inställningar. När konfigurationsinställningarna isoleras efter scenario i separata klasser följer appen två viktiga principer för programvaruteknik:

  • Inkapsling:
    • Klasser som är beroende av konfigurationsinställningar beror bara på de konfigurationsinställningar som de använder.
  • Separation av bekymmer:
    • Inställningar för olika delar av appen är inte beroende av eller kopplade till varandra.

Alternativen tillhandahåller också en mekanism för att verifiera konfigurationsdata. Mer information finns i avsnittet Alternativvalidering .

Det här avsnittet innehåller information om alternativmönstret i ASP.NET Core. Information om hur du använder alternativmönstret i konsolappar finns i Alternativmönster i .NET.

Visa eller ladda ned exempelkod (hur du laddar ned)

Bind hierarkisk konfiguration

Det bästa sättet att läsa relaterade konfigurationsvärden är att använda mönstret alternativ. Om du till exempel vill läsa följande konfigurationsvärden:

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

Skapa följande PositionOptions klass:

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

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

En options-klass:

  • Måste vara icke-abstrakt med en offentlig parameterlös konstruktor.
  • Alla offentliga läs- och skrivegenskaper av typen är bundna.
  • Fält är inte bundna. I föregående kod är inte Position bunden. Egenskapen Position används så att strängen "Position" inte behöver hårdkodas i appen när klassen binds till en konfigurationsprovider.

Följande kod:

  • Anropar ConfigurationBinder.Bind för att binda PositionOptions klassen till avsnittet Position .
  • Visar konfigurationsdata 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}");
    }
}

I föregående kod läses som standard ändringar i JSON-konfigurationsfilen när appen har startats.

ConfigurationBinder.Get<T> binder och returnerar den angivna typen. ConfigurationBinder.Get<T> kan vara bekvämare än att använda ConfigurationBinder.Bind. Följande kod visar hur du använder ConfigurationBinder.Get<T> med PositionOptions klassen:

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

I föregående kod läses som standard ändringar i JSON-konfigurationsfilen när appen har startats.

En alternativ metod när du använder alternativmönstret är att binda Position avsnittet och lägga till det i containern för beroendeinmatningstjänsten. I följande kod PositionOptions läggs till i tjänstcontainern med Configure och är bunden till konfigurationen:

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

Med hjälp av föregående kod läser följande kod positionsalternativen:

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

I föregående kod läss inte ändringar i JSON-konfigurationsfilen när appen har startats. Om du vill läsa ändringar när appen har startats använder du IOptionsSnapshot.

Alternativgränssnitt

IOptions<TOptions>:

IOptionsSnapshot<TOptions>:

IOptionsMonitor<TOptions>:

Scenarier efter konfigurationen gör det möjligt att ställa in eller ändra alternativ när all IConfigureOptions<TOptions> konfiguration har inträffat.

IOptionsFactory<TOptions> ansvarar för att skapa nya alternativinstanser. Den har en enda Create metod. Standardimplementeringen tar alla registrerade IConfigureOptions<TOptions> och IPostConfigureOptions<TOptions> kör alla konfigurationer först, följt av efterkonfigurationen. Den skiljer mellan IConfigureNamedOptions<TOptions> och IConfigureOptions<TOptions> och anropar bara rätt gränssnitt.

IOptionsMonitorCache<TOptions> används av IOptionsMonitor<TOptions> för att cachelagrar TOptions instanser. Ogiltigförklarar IOptionsMonitorCache<TOptions> alternativinstanser i övervakaren så att värdet omberäknas (TryRemove). Värden kan introduceras manuellt med TryAdd. Metoden Clear används när alla namngivna instanser ska återskapas på begäran.

Använda IOptionsSnapshot för att läsa uppdaterade data

Med hjälp IOptionsSnapshot<TOptions>av beräknas alternativen en gång per begäran när de används och cachelagras under begärans livslängd. Ändringar i konfigurationen läse när appen startar när du använder konfigurationsproviders som stöder läsning av uppdaterade konfigurationsvärden.

Skillnaden mellan IOptionsMonitor och IOptionsSnapshot är att:

  • IOptionsMonitor är en Singleton-tjänst som hämtar aktuella alternativvärden när som helst, vilket är särskilt användbart i singleton-beroenden.
  • IOptionsSnapshot är en Scoped-tjänst och ger en ögonblicksbild av alternativen när objektet IOptionsSnapshot<T> konstrueras. Ögonblicksbilder av alternativ är utformade för användning med tillfälliga och begränsade beroenden.

Följande kod använder 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}");
    }
}

Följande kod registrerar en konfigurationsinstans som MyOptions binder mot:

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

    services.AddRazorPages();
}

I föregående kod läss ändringar i JSON-konfigurationsfilen när appen har startats.

IOptionsMonitor

Följande kod registrerar en konfigurationsinstans som MyOptions binder mot.

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

    services.AddRazorPages();
}

I följande exempel används 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}");
    }
}

I föregående kod läses som standard ändringar i JSON-konfigurationsfilen när appen har startats.

Namngivna alternativ stöder användning av IConfigureNamedOptions

Namngivna alternativ:

  • Är användbara när flera konfigurationsavsnitt binder till samma egenskaper.
  • Är skiftlägeskänsliga.

Överväg följande appsettings.json fil:

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

I stället för att skapa två klasser för bindning TopItem:Month och TopItem:Yearanvänds följande klass för varje avsnitt:

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

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

Följande kod konfigurerar de namngivna alternativen:

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

Följande kod visar de namngivna alternativen:

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

Alla alternativ heter instanser. IConfigureOptions<TOptions> instanser behandlas som mål för instansen Options.DefaultName , som är string.Empty. IConfigureNamedOptions<TOptions> implementerar IConfigureOptions<TOptions>också . Standardimplementeringen av IOptionsFactory<TOptions> har logik för att använda var och en på rätt sätt. Det null namngivna alternativet används för att rikta alla namngivna instanser i stället för en specifik namngiven instans. ConfigureAll och PostConfigureAll använd den här konventionen.

OptionsBuilder API

OptionsBuilder<TOptions> används för att konfigurera TOptions instanser. OptionsBuilder effektiviserar skapandet av namngivna alternativ eftersom det bara är en enda parameter till det första AddOptions<TOptions>(string optionsName) anropet i stället för att visas i alla efterföljande anrop. Alternativvalidering och överlagringar ConfigureOptions som accepterar tjänstberoenden är endast tillgängliga via OptionsBuilder.

OptionsBuilder används i avsnittet Alternativvalidering .

Se Använda AddOptions för att konfigurera anpassad databas för information om hur du lägger till en anpassad databas.

Använda DI-tjänster för att konfigurera alternativ

Tjänster kan nås från beroendeinmatning när alternativen konfigureras på två sätt:

  • Skicka ett konfigurationsombud till ConfigureOptionsBuilder<TOptions>. OptionsBuilder<TOptions> Tillhandahåller överlagringar som Configure tillåter användning av upp till fem tjänster för att konfigurera alternativ:

    services.AddOptions<MyOptions>("optionalName")
        .Configure<Service1, Service2, Service3, Service4, Service5>(
            (o, s, s2, s3, s4, s5) => 
                o.Property = DoSomethingWith(s, s2, s3, s4, s5));
    
  • Skapa en typ som implementerar IConfigureOptions<TOptions> eller IConfigureNamedOptions<TOptions> registrerar typen som en tjänst.

Vi rekommenderar att du skickar ett konfigurationsombud till , eftersom det är mer komplext att Configureskapa en tjänst. Att skapa en typ motsvarar vad ramverket gör när det anropar Configure. Anrop Configure registrerar en tillfällig generisk IConfigureNamedOptions<TOptions>, som har en konstruktor som accepterar de angivna allmänna tjänsttyperna.

Alternativvalidering

Alternativvalidering gör att alternativvärden kan verifieras.

Överväg följande appsettings.json fil:

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

Följande klass binder till konfigurationsavsnittet "MyConfig" och tillämpar ett par DataAnnotations regler:

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

Följande kod:

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

Tilläggsmetoden ValidateDataAnnotations definieras i NuGet-paketet Microsoft.Extensions.Options.DataAnnotations . För webbappar som använder Microsoft.NET.Sdk.Web SDK refereras det här paketet implicit från det delade ramverket.

Följande kod visar konfigurationsvärdena eller valideringsfelen:

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

Följande kod tillämpar en mer komplex verifieringsregel med hjälp av ett ombud:

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 komplex validering

Följande klass implementerar 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 Gör det möjligt att flytta valideringskoden från StartUp och till en klass.

Med hjälp av föregående kod aktiveras validering med Startup.ConfigureServices följande kod:

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

Alternativ efter konfiguration

Ange efterkonfiguration med IPostConfigureOptions<TOptions>. Efterkonfigurationen körs efter att all IConfigureOptions<TOptions> konfiguration har utförts:

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

PostConfigure är tillgängligt för efterkonfigurering av namngivna alternativ:

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

Använd PostConfigureAll för att efterkonfigurera alla konfigurationsinstanser:

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

Komma åt alternativ under start

IOptions<TOptions> och IOptionsMonitor<TOptions> kan användas i Startup.Configure, eftersom tjänster skapas innan Configure metoden körs.

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

Använd IOptions<TOptions> inte eller IOptionsMonitor<TOptions> i Startup.ConfigureServices. Det kan finnas ett inkonsekvent alternativtillstånd på grund av ordningen på tjänstregistreringar.

NuGet-paketet Options.ConfigurationExtensions

Paketet Microsoft.Extensions.Options.ConfigurationExtensions refereras implicit till i ASP.NET Core-appar.