Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.
Advertência
Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.
Importante
Estas informações referem-se a um produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado comercialmente. A Microsoft não oferece garantias, expressas ou implícitas, em relação às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
Por Rick Anderson.
O padrão de opções usa classes para fornecer acesso fortemente tipado a grupos de configurações relacionadas. Quando as definições de configuração são isoladas por cenário em classes separadas, o aplicativo adere a dois princípios importantes de engenharia de software:
-
Encapsulamento:
- As classes que dependem das definições de configuração dependem apenas das definições de configuração que utilizam.
-
Separação de preocupações:
- As configurações de diferentes partes do aplicativo não são dependentes ou acopladas umas às outras.
As opções também fornecem um mecanismo para validar dados de configuração. Para obter mais informações, consulte a seção Validação de opções.
Este artigo fornece informações sobre o padrão de opções no ASP.NET Core. Para obter informações sobre como usar o padrão de opções em aplicativos de console, consulte Padrão de opções no .NET.
Vincular configuração hierárquica
A maneira preferida de ler os valores de configuração relacionados é usando o padrão de opções . Por exemplo, para ler os seguintes valores de configuração:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
Crie a seguinte PositionOptions
classe:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; } = String.Empty;
public string Name { get; set; } = String.Empty;
}
Uma classe de opções:
- Deve ser não-abstrato.
- Tem propriedades públicas de leitura-gravação do tipo que têm itens correspondentes na configuração estão vinculados.
- Tem suas propriedades de leitura-gravação vinculadas a entradas correspondentes na configuração.
-
Não tem seus campos vinculados. No código anterior,
Position
não está vinculado. OPosition
campo é usado para que a cadeia de caracteres"Position"
não precise ser codificada no aplicativo ao vincular a classe a um provedor de configuração.
O seguinte código:
- Chama ConfigurationBinder.Bind para vincular a
PositionOptions
classe àPosition
seção. - Exibe os dados de
Position
configuração.
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}");
}
}
No código anterior, por padrão, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
ConfigurationBinder.Get<T>
Vincula e retorna o tipo especificado.
ConfigurationBinder.Get<T>
pode ser mais conveniente do que usar ConfigurationBinder.Bind
. O código a seguir mostra como usar ConfigurationBinder.Get<T>
com a PositionOptions
classe:
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}");
}
}
No código anterior, por padrão, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
Bind também permite a concretização de uma classe abstrata. Considere o seguinte código que usa a classe SomethingWithAName
abstrata :
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;
}
O código a seguir exibe os valores de NameTitleOptions
configuração:
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}"
);
}
}
As chamadas para são menos rigorosas do que as Bind
chamadas para Get<>
:
-
Bind
permite a concretização de um resumo. -
Get<>
tem que criar uma instância própria.
O padrão de opções
Uma abordagem alternativa ao usar o padrão de opções é vincular a Position
seção e adicioná-la ao contêiner do serviço de injeção de dependência. No código a seguir, PositionOptions
é adicionado ao contêiner de serviço com Configure e vinculado à configuração:
using ConfigSample.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
var app = builder.Build();
Usando o código anterior, o código a seguir lê as opções de posição:
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}");
}
}
No código anterior, as alterações no ficheiro de configuração JSON após o início da aplicação não são lidas. Para ler as alterações após o início do aplicativo, use IOptionsSnapshot.
Opções de interfaces
- Não suporta:
- Leitura de dados de configuração após o início do aplicativo.
- Opções nomeadas
- Está registado como Singleton e pode ser injetado em qualquer vida útil.
- É útil em cenários em que as opções devem ser recalculadas em cada solicitação. Para obter mais informações, consulte Usar IOptionsSnapshot para ler dados atualizados.
- Está registrado como Scoped e, portanto, não pode ser injetado em um serviço Singleton .
- Suporta opções nomeadas
- É usado para recuperar opções e gerenciar notificações de opções para
TOptions
instâncias. - Está registado como Singleton e pode ser injetado em qualquer vida útil.
- Apoios:
- Notificações de alteração
- opções nomeadas
- Configuração recarregável
- Invalidação de opções seletivas (IOptionsMonitorCache<TOptions>)
Os cenários pós-configuração permitem definir ou alterar opções depois que toda a IConfigureOptions<TOptions> configuração ocorre.
IOptionsFactory<TOptions> é responsável pela criação de novas instâncias de opções. Tem um único Create método. A implementação padrão leva todos os registrados IConfigureOptions<TOptions> e IPostConfigureOptions<TOptions> executa todas as configurações primeiro, seguido pela pós-configuração. Ele distingue entre IConfigureNamedOptions<TOptions> e IConfigureOptions<TOptions> só chama a interface apropriada.
IOptionsMonitorCache<TOptions> é usado por para armazenar instâncias em IOptionsMonitor<TOptions> cache TOptions
. O IOptionsMonitorCache<TOptions> invalida instâncias de opções no monitor para que o valor seja recalculado (TryRemove). Os valores podem ser introduzidos manualmente com TryAdd. O Clear método é usado quando todas as instâncias nomeadas devem ser recriadas sob demanda.
Use IOptionsSnapshot para ler dados atualizados
Utilização de IOptionsSnapshot<TOptions>:
- As opções são calculadas uma vez por solicitação quando acessadas e armazenadas em cache durante o tempo de vida da solicitação.
- Pode incorrer em uma penalidade de desempenho significativa porque é um serviço com escopo e é recalculado por solicitação. Para obter mais informações, consulte este problema do GitHub e Melhorar o desempenho da vinculação de configuração.
- As alterações na configuração são lidas depois que o aplicativo é iniciado ao usar provedores de configuração que suportam a leitura de valores de configuração atualizados.
A diferença entre IOptionsMonitor
e IOptionsSnapshot
é que:
-
IOptionsMonitor
é um serviço Singleton que recupera valores de opção atuais a qualquer momento, o que é especialmente útil em dependências singleton. -
IOptionsSnapshot
é um serviço com escopo e fornece um instantâneo das opções no momento em que oIOptionsSnapshot<T>
objeto é construído. Os instantâneos de opções são projetados para uso com dependências transitórias e com escopo.
O código a seguir usa IOptionsSnapshot<TOptions>o .
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}");
}
}
O código a seguir registra uma instância de configuração que MyOptions
se liga contra:
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
No código anterior, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
IOptionsMonitor
O código a seguir registra uma instância de configuração que MyOptions
se liga contra.
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
O exemplo a seguir usa 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}");
}
}
No código anterior, por padrão, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
Especifique um nome de chave personalizado para uma propriedade de configuração usando ConfigurationKeyName
Por padrão, os nomes de propriedade da classe options são usados como o nome da chave na fonte de configuração. Se o nome da propriedade for Title
, o nome da chave na configuração também será Title
.
Quando os nomes se diferenciam, você pode usar o ConfigurationKeyName
atributo para especificar o nome da chave na fonte de configuração. Usando essa técnica, você pode mapear uma propriedade na configuração para uma no seu código com um nome diferente.
Isso é útil quando o nome da propriedade na fonte de configuração não é um identificador C# válido ou quando você deseja usar um nome diferente em seu código.
Por exemplo, considere a seguinte classe de opções:
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;
}
As Title
propriedades e Name
class estão vinculadas ao position-title
e position-name
do seguinte appsettings.json
arquivo:
{
"Position": {
"position-title": "Editor",
"position-name": "Joe Smith"
}
}
Suporte a opções nomeadas usando IConfigureNamedOptions
Opções nomeadas:
- São úteis quando várias seções de configuração se ligam às mesmas propriedades.
- Diferenciam maiúsculas de minúsculas.
Considere o seguinte appsettings.json
arquivo:
{
"TopItem": {
"Month": {
"Name": "Green Widget",
"Model": "GW46"
},
"Year": {
"Name": "Orange Gadget",
"Model": "OG35"
}
}
}
Em vez de criar duas classes para vincular TopItem:Month
e TopItem:Year
, a seguinte classe é usada para cada seção:
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;
}
O código a seguir configura as opções nomeadas:
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();
O código a seguir exibe as opções nomeadas:
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" );
}
}
Todas as opções são instâncias nomeadas.
IConfigureOptions<TOptions> as instâncias são tratadas como direcionadas à Options.DefaultName
instância, que é string.Empty
.
IConfigureNamedOptions<TOptions> também implementa IConfigureOptions<TOptions>. A implementação padrão do IOptionsFactory<TOptions> tem lógica para usar cada um adequadamente. A null
opção nomeada é usada para direcionar todas as instâncias nomeadas em vez de uma instância nomeada específica.
ConfigureAll e PostConfigureAll utilizar esta convenção.
OptionsBuilder API
OptionsBuilder<TOptions> é usado para configurar TOptions
instâncias.
OptionsBuilder
simplifica a criação de opções nomeadas, pois é apenas um único parâmetro para a chamada inicial AddOptions<TOptions>(string optionsName)
, em vez de aparecer em todas as chamadas subsequentes. A validação de opções e as ConfigureOptions
sobrecargas que aceitam dependências de serviço só estão disponíveis via OptionsBuilder
.
OptionsBuilder
é usado na seção Validação de opções.
Consulte Usar AddOptions para configurar o repositório personalizado para obter informações sobre como adicionar um repositório personalizado.
Usar serviços de DI para configurar opções
Os serviços podem ser acessados a partir da injeção de dependência durante a configuração de opções de duas maneiras:
Passe um delegado de configuração para Configure o OptionsBuilder<TOptions>.
OptionsBuilder<TOptions>
fornece sobrecargas de Configure que permitem o uso de até cinco serviços para configurar opções: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));
Crie um tipo que implemente IConfigureOptions<TOptions> ou IConfigureNamedOptions<TOptions> e registre o tipo como um serviço.
Recomendamos passar um delegado de configuração para Configureo , já que a criação de um serviço é mais complexa. Criar um tipo é equivalente ao que a estrutura faz ao chamar Configure. A chamada Configure registra um genérico IConfigureNamedOptions<TOptions>transitório , que tem um construtor que aceita os tipos de serviço genéricos especificados.
Validação de opções
A validação de opções permite que os valores das opções sejam validados.
Considere o seguinte appsettings.json
arquivo:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
A classe a seguir é usada para vincular à "MyConfig"
seção de DataAnnotations
configuração e aplica algumas regras:
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; }
}
O seguinte código:
- Chamadas AddOptions para obter um OptionsBuilder<TOptions> que se liga à
MyConfigOptions
classe. - Chamadas ValidateDataAnnotations para habilitar a validação usando
DataAnnotations
o .
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();
O ValidateDataAnnotations
método de extensão é definido no pacote NuGet Microsoft.Extensions.Options.DataAnnotations. Para aplicativos Web que usam o Microsoft.NET.Sdk.Web
SDK, esse pacote é referenciado implicitamente a partir da estrutura compartilhada.
O código a seguir exibe os valores de configuração ou os erros de validação:
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);
}
O código a seguir aplica uma regra de validação mais complexa usando um delegado:
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>
e IValidatableObject
A seguinte classe implementa 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
Permite mover o código de validação de Program.cs
e para uma classe.
Usando o código anterior, a validação é habilitada com Program.cs
o seguinte código:
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();
A validação de opções também suporta IValidatableObject. Para executar a validação em nível de classe de uma classe dentro da própria classe:
- Implemente a
IValidatableObject
interface e seu Validate método dentro da classe. - Ligue ValidateDataAnnotations para
Program.cs
.
ValidateOnStart
A validação de opções é executada na primeira vez que uma TOption
instância é criada. Isso significa, por exemplo, quando o primeiro acesso a IOptionsSnapshot<TOptions>.Value
ocorre em um pipeline de solicitação ou quando IOptionsMonitor<TOptions>.Get(string)
é chamado nas configurações presentes. Depois que as configurações são recarregadas, a validação é executada novamente. O tempo de execução do ASP.NET Core usa OptionsCache<TOptions> para armazenar em cache a instância de opções depois que ela é criada.
Para executar a validação de opções ansiosamente, quando o aplicativo for iniciado, ligue ValidateOnStart<TOptions>(OptionsBuilder<TOptions>)para Program.cs
:
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.ValidateOnStart();
Opções pós-configuração
Defina a pós-configuração com IPostConfigureOptions<TOptions>o . A pós-configuração é executada depois que toda a IConfigureOptions<TOptions> configuração ocorre:
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 está disponível para opções nomeadas pós-configuração:
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();
Use PostConfigureAll para pós-configurar todas as instâncias de configuração:
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";
});
Opções de acesso em Program.cs
Para aceder IOptions<TOptions> ou entrar no IOptionsMonitor<TOptions>, ligue Program.cs
paraGetRequiredServiceWebApplication.Services:
var app = builder.Build();
var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
.CurrentValue.Option1;
Recursos adicionais
Por Kirk Larkin e Rick Anderson.
O padrão de opções usa classes para fornecer acesso fortemente tipado a grupos de configurações relacionadas. Quando as definições de configuração são isoladas por cenário em classes separadas, o aplicativo adere a dois princípios importantes de engenharia de software:
-
Encapsulamento:
- As classes que dependem das definições de configuração dependem apenas das definições de configuração que utilizam.
-
Separação de preocupações:
- As configurações de diferentes partes do aplicativo não são dependentes ou acopladas umas às outras.
As opções também fornecem um mecanismo para validar dados de configuração. Para obter mais informações, consulte a seção Validação de opções.
Este artigo fornece informações sobre o padrão de opções no ASP.NET Core. Para obter informações sobre como usar o padrão de opções em aplicativos de console, consulte Padrão de opções no .NET.
Vincular configuração hierárquica
A maneira preferida de ler os valores de configuração relacionados é usando o padrão de opções . Por exemplo, para ler os seguintes valores de configuração:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
Crie a seguinte PositionOptions
classe:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; } = String.Empty;
public string Name { get; set; } = String.Empty;
}
Uma classe de opções:
- Deve ser não abstrato e ter um construtor público sem parâmetros.
- Todas as propriedades públicas de leitura-gravação do tipo são vinculadas.
- Os campos não estão vinculados. No código anterior,
Position
não está vinculado. OPosition
campo é usado para que a cadeia de caracteres"Position"
não precise ser codificada no aplicativo ao vincular a classe a um provedor de configuração.
O seguinte código:
- Chama ConfigurationBinder.Bind para vincular a
PositionOptions
classe àPosition
seção. - Exibe os dados de
Position
configuração.
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}");
}
}
No código anterior, por padrão, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
ConfigurationBinder.Get<T>
Vincula e retorna o tipo especificado.
ConfigurationBinder.Get<T>
pode ser mais conveniente do que usar ConfigurationBinder.Bind
. O código a seguir mostra como usar ConfigurationBinder.Get<T>
com a PositionOptions
classe:
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}");
}
}
No código anterior, por padrão, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
Uma abordagem alternativa ao usar o padrão de opções é vincular a Position
seção e adicioná-la ao contêiner do serviço de injeção de dependência. No código a seguir, PositionOptions
é adicionado ao contêiner de serviço com Configure e vinculado à configuração:
using ConfigSample.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
var app = builder.Build();
Usando o código anterior, o código a seguir lê as opções de posição:
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}");
}
}
No código anterior, as alterações no ficheiro de configuração JSON após o início da aplicação não são lidas. Para ler as alterações após o início do aplicativo, use IOptionsSnapshot.
Opções de interfaces
- Não suporta:
- Leitura de dados de configuração após o início do aplicativo.
- Opções nomeadas
- Está registado como Singleton e pode ser injetado em qualquer vida útil.
- É útil em cenários em que as opções devem ser recalculadas em cada solicitação. Para obter mais informações, consulte Usar IOptionsSnapshot para ler dados atualizados.
- Está registrado como Scoped e, portanto, não pode ser injetado em um serviço Singleton .
- Suporta opções nomeadas
- É usado para recuperar opções e gerenciar notificações de opções para
TOptions
instâncias. - Está registado como Singleton e pode ser injetado em qualquer vida útil.
- Apoios:
- Notificações de alteração
- opções nomeadas
- Configuração recarregável
- Invalidação de opções seletivas (IOptionsMonitorCache<TOptions>)
Os cenários pós-configuração permitem definir ou alterar opções depois que toda a IConfigureOptions<TOptions> configuração ocorre.
IOptionsFactory<TOptions> é responsável pela criação de novas instâncias de opções. Tem um único Create método. A implementação padrão leva todos os registrados IConfigureOptions<TOptions> e IPostConfigureOptions<TOptions> executa todas as configurações primeiro, seguido pela pós-configuração. Ele distingue entre IConfigureNamedOptions<TOptions> e IConfigureOptions<TOptions> só chama a interface apropriada.
IOptionsMonitorCache<TOptions> é usado por para armazenar instâncias em IOptionsMonitor<TOptions> cache TOptions
. O IOptionsMonitorCache<TOptions> invalida instâncias de opções no monitor para que o valor seja recalculado (TryRemove). Os valores podem ser introduzidos manualmente com TryAdd. O Clear método é usado quando todas as instâncias nomeadas devem ser recriadas sob demanda.
Use IOptionsSnapshot para ler dados atualizados
Utilização de IOptionsSnapshot<TOptions>:
- As opções são calculadas uma vez por solicitação quando acessadas e armazenadas em cache durante o tempo de vida da solicitação.
- Pode incorrer em uma penalidade de desempenho significativa porque é um serviço com escopo e é recalculado por solicitação. Para obter mais informações, consulte este problema do GitHub e Melhorar o desempenho da vinculação de configuração.
- As alterações na configuração são lidas depois que o aplicativo é iniciado ao usar provedores de configuração que suportam a leitura de valores de configuração atualizados.
A diferença entre IOptionsMonitor
e IOptionsSnapshot
é que:
-
IOptionsMonitor
é um serviço Singleton que recupera valores de opção atuais a qualquer momento, o que é especialmente útil em dependências singleton. -
IOptionsSnapshot
é um serviço com escopo e fornece um instantâneo das opções no momento em que oIOptionsSnapshot<T>
objeto é construído. Os instantâneos de opções são projetados para uso com dependências transitórias e com escopo.
O código a seguir usa IOptionsSnapshot<TOptions>o .
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}");
}
}
O código a seguir registra uma instância de configuração que MyOptions
se liga contra:
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
No código anterior, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
IOptionsMonitor
O código a seguir registra uma instância de configuração que MyOptions
se liga contra.
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
O exemplo a seguir usa 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}");
}
}
No código anterior, por padrão, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
Suporte a opções nomeadas usando IConfigureNamedOptions
Opções nomeadas:
- São úteis quando várias seções de configuração se ligam às mesmas propriedades.
- Diferenciam maiúsculas de minúsculas.
Considere o seguinte appsettings.json
arquivo:
{
"TopItem": {
"Month": {
"Name": "Green Widget",
"Model": "GW46"
},
"Year": {
"Name": "Orange Gadget",
"Model": "OG35"
}
}
}
Em vez de criar duas classes para vincular TopItem:Month
e TopItem:Year
, a seguinte classe é usada para cada seção:
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;
}
O código a seguir configura as opções nomeadas:
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();
O código a seguir exibe as opções nomeadas:
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" );
}
}
Todas as opções são instâncias nomeadas.
IConfigureOptions<TOptions> as instâncias são tratadas como direcionadas à Options.DefaultName
instância, que é string.Empty
.
IConfigureNamedOptions<TOptions> também implementa IConfigureOptions<TOptions>. A implementação padrão do IOptionsFactory<TOptions> tem lógica para usar cada um adequadamente. A null
opção nomeada é usada para direcionar todas as instâncias nomeadas em vez de uma instância nomeada específica.
ConfigureAll e PostConfigureAll utilizar esta convenção.
OptionsBuilder API
OptionsBuilder<TOptions> é usado para configurar TOptions
instâncias.
OptionsBuilder
simplifica a criação de opções nomeadas, pois é apenas um único parâmetro para a chamada inicial AddOptions<TOptions>(string optionsName)
, em vez de aparecer em todas as chamadas subsequentes. A validação de opções e as ConfigureOptions
sobrecargas que aceitam dependências de serviço só estão disponíveis via OptionsBuilder
.
OptionsBuilder
é usado na seção Validação de opções.
Consulte Usar AddOptions para configurar o repositório personalizado para obter informações sobre como adicionar um repositório personalizado.
Usar serviços de DI para configurar opções
Os serviços podem ser acessados a partir da injeção de dependência durante a configuração de opções de duas maneiras:
Passe um delegado de configuração para Configure o OptionsBuilder<TOptions>.
OptionsBuilder<TOptions>
fornece sobrecargas de Configure que permitem o uso de até cinco serviços para configurar opções: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));
Crie um tipo que implemente IConfigureOptions<TOptions> ou IConfigureNamedOptions<TOptions> e registre o tipo como um serviço.
Recomendamos passar um delegado de configuração para Configureo , já que a criação de um serviço é mais complexa. Criar um tipo é equivalente ao que a estrutura faz ao chamar Configure. A chamada Configure registra um genérico IConfigureNamedOptions<TOptions>transitório , que tem um construtor que aceita os tipos de serviço genéricos especificados.
Validação de opções
A validação de opções permite que os valores das opções sejam validados.
Considere o seguinte appsettings.json
arquivo:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
A classe a seguir é usada para vincular à "MyConfig"
seção de DataAnnotations
configuração e aplica algumas regras:
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; }
}
O seguinte código:
- Chamadas AddOptions para obter um OptionsBuilder<TOptions> que se liga à
MyConfigOptions
classe. - Chamadas ValidateDataAnnotations para habilitar a validação usando
DataAnnotations
o .
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();
O ValidateDataAnnotations
método de extensão é definido no pacote NuGet Microsoft.Extensions.Options.DataAnnotations. Para aplicativos Web que usam o Microsoft.NET.Sdk.Web
SDK, esse pacote é referenciado implicitamente a partir da estrutura compartilhada.
O código a seguir exibe os valores de configuração ou os erros de validação:
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);
}
O código a seguir aplica uma regra de validação mais complexa usando um delegado:
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>
e IValidatableObject
A seguinte classe implementa 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
Permite mover o código de validação de Program.cs
e para uma classe.
Usando o código anterior, a validação é habilitada com Program.cs
o seguinte código:
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();
A validação de opções também suporta IValidatableObject. Para executar a validação em nível de classe de uma classe dentro da própria classe:
- Implemente a
IValidatableObject
interface e seu Validate método dentro da classe. - Ligue ValidateDataAnnotations para
Program.cs
.
ValidateOnStart
A validação de opções é executada na primeira vez que uma IOptions<TOptions>implementação , IOptionsSnapshot<TOptions>ou IOptionsMonitor<TOptions> é criada. Para executar a validação de opções ansiosamente, quando o aplicativo for iniciado, ligue ValidateOnStart para Program.cs
:
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.ValidateOnStart();
Opções pós-configuração
Defina a pós-configuração com IPostConfigureOptions<TOptions>o . A pós-configuração é executada depois que toda a IConfigureOptions<TOptions> configuração ocorre:
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 está disponível para opções nomeadas pós-configuração:
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();
Use PostConfigureAll para pós-configurar todas as instâncias de configuração:
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";
});
Opções de acesso em Program.cs
Para aceder IOptions<TOptions> ou entrar no IOptionsMonitor<TOptions>, ligue Program.cs
paraGetRequiredServiceWebApplication.Services:
var app = builder.Build();
var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
.CurrentValue.Option1;
Recursos adicionais
Por Kirk Larkin e Rick Anderson.
O padrão de opções usa classes para fornecer acesso fortemente tipado a grupos de configurações relacionadas. Quando as definições de configuração são isoladas por cenário em classes separadas, o aplicativo adere a dois princípios importantes de engenharia de software:
-
Encapsulamento:
- As classes que dependem das definições de configuração dependem apenas das definições de configuração que utilizam.
-
Separação de preocupações:
- As configurações de diferentes partes do aplicativo não são dependentes ou acopladas umas às outras.
As opções também fornecem um mecanismo para validar dados de configuração. Para obter mais informações, consulte a seção Validação de opções.
Este tópico fornece informações sobre o padrão de opções no ASP.NET Core. Para obter informações sobre como usar o padrão de opções em aplicativos de console, consulte Padrão de opções no .NET.
Visualizar ou descarregar amostra de código (como descarregar)
Vincular configuração hierárquica
A maneira preferida de ler os valores de configuração relacionados é usando o padrão de opções . Por exemplo, para ler os seguintes valores de configuração:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
Crie a seguinte PositionOptions
classe:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; }
public string Name { get; set; }
}
Uma classe de opções:
- Deve ser não abstrato e ter um construtor público sem parâmetros.
- Todas as propriedades públicas de leitura-gravação do tipo são vinculadas.
- Os campos não estão vinculados. No código anterior,
Position
não está vinculado. APosition
propriedade é usada para que a cadeia de caracteres"Position"
não precise ser codificada no aplicativo ao vincular a classe a um provedor de configuração.
O seguinte código:
- Chama ConfigurationBinder.Bind para vincular a
PositionOptions
classe àPosition
seção. - Exibe os dados de
Position
configuração.
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}");
}
}
No código anterior, por padrão, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
ConfigurationBinder.Get<T>
Vincula e retorna o tipo especificado.
ConfigurationBinder.Get<T>
pode ser mais conveniente do que usar ConfigurationBinder.Bind
. O código a seguir mostra como usar ConfigurationBinder.Get<T>
com a PositionOptions
classe:
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}");
}
}
No código anterior, por padrão, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
Uma abordagem alternativa ao usar o padrão de opções é vincular a Position
seção e adicioná-la ao contêiner do serviço de injeção de dependência. No código a seguir, PositionOptions
é adicionado ao contêiner de serviço com Configure e vinculado à configuração:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(Configuration.GetSection(
PositionOptions.Position));
services.AddRazorPages();
}
Usando o código anterior, o código a seguir lê as opções de posição:
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}");
}
}
No código anterior, as alterações no ficheiro de configuração JSON após o início da aplicação não são lidas. Para ler as alterações após o início do aplicativo, use IOptionsSnapshot.
Opções de interfaces
- Não suporta:
- Leitura de dados de configuração após o início do aplicativo.
- Opções nomeadas
- Está registado como Singleton e pode ser injetado em qualquer vida útil.
- É útil em cenários em que as opções devem ser recalculadas em cada solicitação. Para obter mais informações, consulte Usar IOptionsSnapshot para ler dados atualizados.
- Está registrado como Scoped e, portanto, não pode ser injetado em um serviço Singleton .
- Suporta opções nomeadas
- É usado para recuperar opções e gerenciar notificações de opções para
TOptions
instâncias. - Está registado como Singleton e pode ser injetado em qualquer vida útil.
- Apoios:
- Notificações de alteração
- Opções nomeadas
- Configuração recarregável
- Invalidação de opções seletivas (IOptionsMonitorCache<TOptions>)
Os cenários pós-configuração permitem definir ou alterar opções depois que toda a IConfigureOptions<TOptions> configuração ocorre.
IOptionsFactory<TOptions> é responsável pela criação de novas instâncias de opções. Tem um único Create método. A implementação padrão leva todos os registrados IConfigureOptions<TOptions> e IPostConfigureOptions<TOptions> executa todas as configurações primeiro, seguido pela pós-configuração. Ele distingue entre IConfigureNamedOptions<TOptions> e IConfigureOptions<TOptions> só chama a interface apropriada.
IOptionsMonitorCache<TOptions> é usado por para armazenar instâncias em IOptionsMonitor<TOptions> cache TOptions
. O IOptionsMonitorCache<TOptions> invalida instâncias de opções no monitor para que o valor seja recalculado (TryRemove). Os valores podem ser introduzidos manualmente com TryAdd. O Clear método é usado quando todas as instâncias nomeadas devem ser recriadas sob demanda.
Use IOptionsSnapshot para ler dados atualizados
Usando IOptionsSnapshot<TOptions>o , as opções são calculadas uma vez por solicitação quando acessadas e armazenadas em cache durante o tempo de vida da solicitação. As alterações na configuração são lidas depois que o aplicativo é iniciado ao usar provedores de configuração que suportam a leitura de valores de configuração atualizados.
A diferença entre IOptionsMonitor
e IOptionsSnapshot
é que:
-
IOptionsMonitor
é um serviço Singleton que recupera valores de opção atuais a qualquer momento, o que é especialmente útil em dependências singleton. -
IOptionsSnapshot
é um serviço com escopo e fornece um instantâneo das opções no momento em que oIOptionsSnapshot<T>
objeto é construído. Os instantâneos de opções são projetados para uso com dependências transitórias e com escopo.
O código a seguir usa IOptionsSnapshot<TOptions>o .
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}");
}
}
O código a seguir registra uma instância de configuração que MyOptions
se liga contra:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddRazorPages();
}
No código anterior, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
IOptionsMonitor
O código a seguir registra uma instância de configuração que MyOptions
se liga contra.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddRazorPages();
}
O exemplo a seguir usa 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}");
}
}
No código anterior, por padrão, as alterações no arquivo de configuração JSON após o início do aplicativo são lidas.
Suporte a opções nomeadas usando IConfigureNamedOptions
Opções nomeadas:
- São úteis quando várias seções de configuração se ligam às mesmas propriedades.
- Diferenciam maiúsculas de minúsculas.
Considere o seguinte appsettings.json
arquivo:
{
"TopItem": {
"Month": {
"Name": "Green Widget",
"Model": "GW46"
},
"Year": {
"Name": "Orange Gadget",
"Model": "OG35"
}
}
}
Em vez de criar duas classes para vincular TopItem:Month
e TopItem:Year
, a seguinte classe é usada para cada seção:
public class TopItemSettings
{
public const string Month = "Month";
public const string Year = "Year";
public string Name { get; set; }
public string Model { get; set; }
}
O código a seguir configura as opções nomeadas:
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();
}
O código a seguir exibe as opções nomeadas:
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" );
}
}
Todas as opções são instâncias nomeadas.
IConfigureOptions<TOptions> as instâncias são tratadas como direcionadas à Options.DefaultName
instância, que é string.Empty
.
IConfigureNamedOptions<TOptions> também implementa IConfigureOptions<TOptions>. A implementação padrão do IOptionsFactory<TOptions> tem lógica para usar cada um adequadamente. A null
opção nomeada é usada para direcionar todas as instâncias nomeadas em vez de uma instância nomeada específica.
ConfigureAll e PostConfigureAll utilizar esta convenção.
OptionsBuilder API
OptionsBuilder<TOptions> é usado para configurar TOptions
instâncias.
OptionsBuilder
simplifica a criação de opções nomeadas, pois é apenas um único parâmetro para a chamada inicial AddOptions<TOptions>(string optionsName)
, em vez de aparecer em todas as chamadas subsequentes. A validação de opções e as ConfigureOptions
sobrecargas que aceitam dependências de serviço só estão disponíveis via OptionsBuilder
.
OptionsBuilder
é usado na seção Validação de opções.
Consulte Usar AddOptions para configurar o repositório personalizado para obter informações sobre como adicionar um repositório personalizado.
Usar serviços de DI para configurar opções
Os serviços podem ser acessados a partir da injeção de dependência durante a configuração de opções de duas maneiras:
Passe um delegado de configuração para Configure o OptionsBuilder<TOptions>.
OptionsBuilder<TOptions>
fornece sobrecargas de Configure que permitem o uso de até cinco serviços para configurar opções:services.AddOptions<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));
Crie um tipo que implemente IConfigureOptions<TOptions> ou IConfigureNamedOptions<TOptions> e registre o tipo como um serviço.
Recomendamos passar um delegado de configuração para Configureo , já que a criação de um serviço é mais complexa. Criar um tipo é equivalente ao que a estrutura faz ao chamar Configure. A chamada Configure registra um genérico IConfigureNamedOptions<TOptions>transitório , que tem um construtor que aceita os tipos de serviço genéricos especificados.
Validação de opções
A validação de opções permite que os valores das opções sejam validados.
Considere o seguinte appsettings.json
arquivo:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
A classe a seguir se liga à "MyConfig"
seção de DataAnnotations
configuração e aplica algumas regras:
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; }
}
O seguinte código:
- Chamadas AddOptions para obter um OptionsBuilder<TOptions> que se liga à
MyConfigOptions
classe. - Chamadas ValidateDataAnnotations para habilitar a validação usando
DataAnnotations
o .
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();
}
O ValidateDataAnnotations
método de extensão é definido no pacote NuGet Microsoft.Extensions.Options.DataAnnotations. Para aplicativos Web que usam o Microsoft.NET.Sdk.Web
SDK, esse pacote é referenciado implicitamente a partir da estrutura compartilhada.
O código a seguir exibe os valores de configuração ou os erros de validação:
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);
}
O código a seguir aplica uma regra de validação mais complexa usando um delegado:
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 para validação complexa
A seguinte classe implementa 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
Permite mover o código de validação de StartUp
e para uma classe.
Usando o código anterior, a validação é habilitada com Startup.ConfigureServices
o seguinte código:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyConfigOptions>(Configuration.GetSection(
MyConfigOptions.MyConfig));
services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions
<MyConfigOptions>, MyConfigValidation>());
services.AddControllersWithViews();
}
Opções pós-configuração
Defina a pós-configuração com IPostConfigureOptions<TOptions>o . A pós-configuração é executada depois que toda a IConfigureOptions<TOptions> configuração ocorre:
services.PostConfigure<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
PostConfigure está disponível para opções nomeadas pós-configuração:
services.PostConfigure<MyOptions>("named_options_1", myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Use PostConfigureAll para pós-configurar todas as instâncias de configuração:
services.PostConfigureAll<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Aceder a opções durante o arranque
IOptions<TOptions> e IOptionsMonitor<TOptions> pode ser usado no Startup.Configure
, uma vez que os serviços são criados antes da execução do Configure
método.
public void Configure(IApplicationBuilder app,
IOptionsMonitor<MyOptions> optionsAccessor)
{
var option1 = optionsAccessor.CurrentValue.Option1;
}
Não use IOptions<TOptions> ou IOptionsMonitor<TOptions> em Startup.ConfigureServices
. Um estado de opções inconsistente pode existir devido à ordem de registros de serviço.
Options.ConfigurationExtensions pacote NuGet
O pacote Microsoft.Extensions.Options.ConfigurationExtensions é implicitamente referenciado em aplicativos ASP.NET Core.