Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Com a ajuda da injeção de dependência, registar os seus serviços e as suas configurações correspondentes podem utilizar o padrão de opções. O padrão de opções permite que os consumidores de sua biblioteca (e seus serviços) exijam instâncias de interfaces de opções onde TOptions está sua classe de opções. O consumo de opções de configuração por meio de objetos fortemente tipados ajuda a garantir uma representação de valor consistente, permite a validação com anotações de dados e elimina a carga de analisar manualmente valores de cadeia de caracteres. Há muitos provedores de configuração para os consumidores da sua biblioteca usarem. Com esses provedores, os consumidores podem configurar sua biblioteca de várias maneiras.
Como autor de uma biblioteca .NET, você aprenderá orientações gerais sobre como expor corretamente o padrão de opções aos consumidores da sua biblioteca. Há várias maneiras de conseguir a mesma coisa, e várias considerações a fazer.
Convenções de nomenclatura
Por convenção, os métodos de extensão responsáveis pelo registro de serviços são nomeados Add{Service}, onde {Service} é um nome significativo e descritivo.
Add{Service} métodos de extensão são comuns no ASP.NET Core e no .NET.
✔️ CONSIDERE nomes que desambiguem seu serviço de outras ofertas.
❌ NÃO use nomes que já fazem parte do ecossistema .NET de pacotes oficiais da Microsoft.
✔️ CONSIDERE nomear classes estáticas que expõem métodos de extensão como {Type}Extensions, onde {Type} é o tipo que você está estendendo.
Diretrizes de namespace
Os pacotes da Microsoft usam o Microsoft.Extensions.DependencyInjection namespace para unificar o registro de várias ofertas de serviço.
✔️ CONSIDERE um namespace que identifique claramente sua oferta de pacote.
❌ NÃO use o namespace para pacotes não oficiais da Microsoft.Extensions.DependencyInjection Microsoft.
Sem parâmetros
Se o serviço puder funcionar com pouca ou nenhuma configuração explícita, considere um método de extensão sem parâmetros.
using Microsoft.Extensions.DependencyInjection;
namespace ExampleLibrary.Extensions.DependencyInjection;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services)
{
services.AddOptions<LibraryOptions>()
.Configure(options =>
{
// Specify default option values
});
// Register lib services here...
// services.AddScoped<ILibraryService, DefaultLibraryService>();
return services;
}
}
No código anterior, o AddMyLibraryService:
- Estende uma instância de IServiceCollection
- Chamadas OptionsServiceCollectionExtensions.AddOptions<TOptions>(IServiceCollection) com o parâmetro type de
LibraryOptions - Encadeia uma chamada para Configure, que especifica os valores de opção padrão
parâmetro IConfiguration
Ao criar uma biblioteca que expõe muitas opções aos consumidores, convém considerar a necessidade de um IConfiguration método de extensão de parâmetro. A instância esperada IConfiguration deve ser delimitada a uma seção nomeada da configuração utilizando a função IConfiguration.GetSection.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace ExampleLibrary.Extensions.DependencyInjection;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services,
IConfiguration namedConfigurationSection)
{
// Default library options are overridden
// by bound configuration values.
services.Configure<LibraryOptions>(namedConfigurationSection);
// Register lib services here...
// services.AddScoped<ILibraryService, DefaultLibraryService>();
return services;
}
}
Sugestão
O Configure<TOptions>(IServiceCollection, IConfiguration) método faz parte do Microsoft.Extensions.Options.ConfigurationExtensions pacote NuGet.
No código anterior, o AddMyLibraryService:
- Estende uma instância de IServiceCollection
- Define um IConfiguration parâmetro
namedConfigurationSection - Chamadas Configure<TOptions>(IServiceCollection, IConfiguration) que passam o parâmetro de tipo genérico de
LibraryOptionse a instâncianamedConfigurationSectiona configurar
Os consumidores nesse padrão fornecem a instância com IConfiguration escopo da seção nomeada:
using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMyLibraryService(
builder.Configuration.GetSection("LibraryOptions"));
using IHost host = builder.Build();
// Application code should start here.
await host.RunAsync();
A chamada para .AddMyLibraryService é feita no IServiceCollection tipo.
Como autor da biblioteca, a especificação de valores padrão depende de você.
Observação
É possível vincular a configuração a uma instância de opções. No entanto, há um risco de colisões de nomes - o que causará erros. Além disso, ao vincular manualmente dessa forma, você limita o consumo do padrão de opções para leitura única. As alterações nas configurações não serão redirecionadas, pois esses consumidores não poderão usar a interface IOptionsMonitor .
services.AddOptions<LibraryOptions>()
.Configure<IConfiguration>(
(options, configuration) =>
configuration.GetSection("LibraryOptions").Bind(options));
Em vez disso, você deve usar o BindConfiguration método de extensão. Esse método de extensão vincula a configuração à instância de opções e também registra uma fonte de token de alteração para a seção de configuração. Isso permite que os consumidores usem a interface IOptionsMonitor .
Parâmetro de caminho da seção de configuração
Os consumidores da sua biblioteca podem querer especificar o caminho da seção de configuração para vincular seu tipo subjacente TOptions . Nesse cenário, você define um string parâmetro em seu método de extensão.
using Microsoft.Extensions.DependencyInjection;
namespace ExampleLibrary.Extensions.DependencyInjection;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services,
string configSectionPath)
{
services.AddOptions<SupportOptions>()
.BindConfiguration(configSectionPath)
.ValidateDataAnnotations()
.ValidateOnStart();
// Register lib services here...
// services.AddScoped<ILibraryService, DefaultLibraryService>();
return services;
}
}
No código anterior, o AddMyLibraryService:
- Estende uma instância de IServiceCollection
- Define um
stringparâmetroconfigSectionPath - Chamadas:
-
AddOptions com o parâmetro de tipo genérico de
SupportOptions -
BindConfigurationcom o parâmetro dado
configSectionPath - ValidateDataAnnotations Para habilitar a validação de anotação de dados
- ValidateOnStart Para impor a validação no início em vez de em tempo de execução
-
AddOptions com o parâmetro de tipo genérico de
No próximo exemplo, o pacote NuGet Microsoft.Extensions.Options.DataAnnotations é usado para habilitar a validação de anotação de dados. A SupportOptions classe é definida da seguinte forma:
using System.ComponentModel.DataAnnotations;
public sealed class SupportOptions
{
[Url]
public string? Url { get; set; }
[Required, EmailAddress]
public required string Email { get; set; }
[Required, DataType(DataType.PhoneNumber)]
public required string PhoneNumber { get; set; }
}
Imagine que o seguinte arquivo JSON appsettings.json é usado:
{
"Support": {
"Url": "https://support.example.com",
"Email": "help@support.example.com",
"PhoneNumber": "+1(888)-SUPPORT"
}
}
parâmetro Action<TOptions>
Os consumidores de sua biblioteca podem estar interessados em fornecer uma expressão lambda que produza uma instância de sua classe de opções. Nesse cenário, você define um Action<LibraryOptions> parâmetro em seu método de extensão.
using Microsoft.Extensions.DependencyInjection;
namespace ExampleLibrary.Extensions.DependencyInjection;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services,
Action<LibraryOptions> configureOptions)
{
services.Configure(configureOptions);
// Register lib services here...
// services.AddScoped<ILibraryService, DefaultLibraryService>();
return services;
}
}
No código anterior, o AddMyLibraryService:
- Estende uma instância de IServiceCollection
- Define um Action<T> parâmetro
configureOptionsondeTéLibraryOptions - Chamadas Configure com base na
configureOptionsação
Os consumidores neste padrão fornecem uma expressão lambda (ou um delegado que satisfaz o parâmetro Action<LibraryOptions>):
using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMyLibraryService(options =>
{
// User defined option values
// options.SomePropertyValue = ...
});
using IHost host = builder.Build();
// Application code should start here.
await host.RunAsync();
Parâmetro de instância de opções
Utilizadores da sua biblioteca podem preferir fornecer uma instância de opções integrada. Nesse cenário, você expõe um método de extensão que usa uma instância do seu objeto options, LibraryOptions.
using Microsoft.Extensions.DependencyInjection;
namespace ExampleLibrary.Extensions.DependencyInjection;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services,
LibraryOptions userOptions)
{
services.AddOptions<LibraryOptions>()
.Configure(options =>
{
// Overwrite default option values
// with the user provided options.
// options.SomeValue = userOptions.SomeValue;
});
// Register lib services here...
// services.AddScoped<ILibraryService, DefaultLibraryService>();
return services;
}
}
No código anterior, o AddMyLibraryService:
- Estende uma instância de IServiceCollection
- Chamadas OptionsServiceCollectionExtensions.AddOptions<TOptions>(IServiceCollection) com o parâmetro type de
LibraryOptions - Encadeia uma chamada para Configure, que especifica valores de opção padrão que podem ser substituídos a partir de determinada
userOptionsinstância
Os consumidores nesse padrão fornecem uma instância da classe LibraryOptions, definindo os valores de propriedade desejados inseridos inline.
using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMyLibraryService(new LibraryOptions
{
// Specify option values
// SomePropertyValue = ...
});
using IHost host = builder.Build();
// Application code should start here.
await host.RunAsync();
Configuração posterior
Depois que todos os valores de opção de configuração forem vinculados ou especificados, a funcionalidade pós-configuração estará disponível. Expondo o mesmo Action<TOptions> parâmetro detalhado anteriormente, você pode optar por chamar PostConfigure. A pós-configuração é executada após todas as .Configure chamadas. Há algumas razões pelas quais você gostaria de considerar o uso PostConfigure:
-
Ordem de execução: Você pode substituir quaisquer valores de configuração que foram definidos nas
.Configurechamadas. - Validação: Você pode validar que os valores padrão foram definidos depois que todas as outras configurações foram aplicadas.
using Microsoft.Extensions.DependencyInjection;
namespace ExampleLibrary.Extensions.DependencyInjection;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services,
Action<LibraryOptions> configureOptions)
{
services.PostConfigure(configureOptions);
// Register lib services here...
// services.AddScoped<ILibraryService, DefaultLibraryService>();
return services;
}
}
No código anterior, o AddMyLibraryService:
- Estende uma instância de IServiceCollection
- Define um Action<T> parâmetro
configureOptionsondeTéLibraryOptions - Chamadas PostConfigure com base na
configureOptionsação
Os consumidores nesse padrão fornecem uma expressão lambda (ou um delegado que satisfaz o Action<LibraryOptions> parâmetro), assim como fariam com o Action<TOptions> parâmetro em um cenário de configuração não-post:
using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMyLibraryService(options =>
{
// Specify option values
// options.SomePropertyValue = ...
});
using IHost host = builder.Build();
// Application code should start here.
await host.RunAsync();