Patrón de opciones en ASP.NET Core
Por Kirk Larkin y Rick Anderson.
El patrón de opciones usa clases para proporcionar acceso fuertemente tipado a grupos de configuraciones relacionadas. Cuando los valores de configuración están aislados por escenario en clases independientes, la aplicación se ajusta a dos principios de ingeniería de software importantes:
- El principio de segregación de interfaz (ISP) o encapsulación: los escenarios (clases) que dependen de valores de configuración dependen únicamente de los valores de configuración que usen.
- Separación de intereses: los valores de configuración para distintos elementos de la aplicación no son dependientes entre sí ni están emparejados.
Las opciones también proporcionan un mecanismo para validar los datos de configuración. Para obtener más información, consulte la sección Opciones de validación.
En este tema se proporciona información sobre el patrón de opciones de ASP.NET Core. Para más información sobre el uso del patrón de opciones en las aplicaciones de consola, consulte Patrón de opciones en .NET.
Vea o descargue el código de ejemplo (cómo descargarlo)
Enlace de configuración jerárquica
La mejor manera de leer valores de configuración relacionados es usar el patrón de opciones. Por ejemplo, para leer los siguientes valores de configuración:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
Cree la siguiente clase PositionOptions:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; }
public string Name { get; set; }
}
Una clase de opciones:
- Debe ser no abstracta con un constructor público sin parámetros.
- Todas las propiedades de lectura y escritura públicas del tipo están enlazadas.
- Los campos no se enlazan. En el código anterior,
Positionno está enlazado. La propiedadPositionse usa para que la cadena"Position"no tenga que estar codificada de forma rígida en la aplicación al enlazar la clase a un proveedor de configuración.
El código siguiente:
- Llama a ConfigurationBinder.Bind para enlazar la clase
PositionOptionsa la secciónPosition. - Muestra los datos de configuración de
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}");
}
}
En el código anterior, de forma predeterminada, los cambios en el archivo de configuración de JSON producidos una vez iniciada la aplicación se leen.
ConfigurationBinder.Get<T> enlaza y devuelve el tipo especificado. Puede ser más conveniente usar ConfigurationBinder.Get<T> que ConfigurationBinder.Bind. En el código siguiente se muestra cómo puede usar ConfigurationBinder.Get<T> con la clase PositionOptions:
public class Test21Model : PageModel
{
private readonly IConfiguration Configuration;
public PositionOptions positionOptions { get; private set; }
public Test21Model(IConfiguration configuration)
{
Configuration = configuration;
}
public ContentResult OnGet()
{
positionOptions = Configuration.GetSection(PositionOptions.Position)
.Get<PositionOptions>();
return Content($"Title: {positionOptions.Title} \n" +
$"Name: {positionOptions.Name}");
}
}
En el código anterior, de forma predeterminada, los cambios en el archivo de configuración de JSON producidos una vez iniciada la aplicación se leen.
Un enfoque alternativo a la hora de usar el *patrón de opciones _ consiste en enlazar la sección Position y agregarla al contenedor del servicio de inserción de dependencias. En el siguiente código se agrega PositionOptions al contenedor de servicios con <xref:Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure_> y se enlaza a la configuración:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(Configuration.GetSection(
PositionOptions.Position));
services.AddRazorPages();
}
A partir del código anterior, el siguiente código lee las opciones de posición:
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}");
}
}
En el código anterior, los cambios en el archivo de configuración de JSON producidos una vez iniciada la aplicación no se leen. Para leer los cambios una vez iniciada la aplicación, use IOptionsSnapshot.
Interfaces de opciones
- No admite:
- Lectura de los datos de configuración una vez iniciada la aplicación.
- Opciones con nombre
- Se registra como Singleton y se puede insertar en cualquier duración del servicio.
- es útil en escenarios donde se deben volver a calcular las opciones en cada solicitud. Para obtener más información, consulte el apartado Uso de IOptionsSnapshot para leer datos actualizados.
- Se registra como Con ámbito y, por tanto, no se puede insertar en un servicio Singleton.
- Admite opciones con nombre
- se usa para recuperar las opciones y administrar las notificaciones de las opciones para instancias de
TOptions. - Se registra como Singleton y se puede insertar en cualquier duración del servicio.
- Es compatible con:
- Notificaciones de cambios
- Opciones con nombre
- Configuración que se puede recargar
- Invalidación de opciones de selección (IOptionsMonitorCache<TOptions>)
Los escenarios posteriores a la configuración permiten establecer o cambiar las opciones después de que finalice toda la configuración de IConfigureOptions<TOptions>.
IOptionsFactory<TOptions> es responsable de crear nuevas instancias de opciones. Tiene un solo método Create. La implementación predeterminada toma todas las instancias registradas de IConfigureOptions<TOptions> y IPostConfigureOptions<TOptions>, y establece todas las configuraciones primero, seguidas de las configuraciones posteriores. Distingue entre IConfigureNamedOptions<TOptions> y IConfigureOptions<TOptions>, y solo llama a la interfaz adecuada.
IOptionsMonitorCache<TOptions> se usa por IOptionsMonitor<TOptions> para almacenar en caché las instancias de TOptions. IOptionsMonitorCache<TOptions> invalida instancias de opciones en la supervisión para que se pueda volver a calcular el valor (TryRemove). Los valores se pueden introducir manualmente y mediante TryAdd. Se usa el método Clear cuando todas las instancias con nombre se deben volver a crear a petición.
Uso de IOptionsSnapshot para leer datos actualizados
Al usar IOptionsSnapshot<TOptions>, cuando se accede a las opciones y se las almacena en caché durante la vigencia de la solicitud, se calculan una vez por solicitud. Los cambios en la configuración se leen tras iniciarse la aplicación al usar proveedores de configuración que admiten la lectura de valores de configuración actualizados.
La diferencia entre IOptionsMonitor y IOptionsSnapshot es que:
IOptionsMonitores un servicio singleton que recupera los valores de las opciones actuales en cualquier momento, lo que resulta especialmente útil en las dependencias singleton.IOptionsSnapshotes un servicio con ámbito y proporciona una instantánea de las opciones en el momento en que se construye el objetoIOptionsSnapshot<T>. Las instantáneas de opciones están diseñadas para usarlas con dependencias transitorias y con ámbito.
El código siguiente usa 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}");
}
}
El código siguiente registra una instancia de configuración en la que se enlaza MyOptions:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddRazorPages();
}
En el código anterior, los cambios en el archivo de configuración de JSON producidos una vez iniciada la aplicación se leen.
IOptionsMonitor
El código siguiente registra una instancia de configuración en la que se enlaza MyOptions.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddRazorPages();
}
En el ejemplo siguiente se 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}");
}
}
En el código anterior, de forma predeterminada, los cambios en el archivo de configuración de JSON producidos una vez iniciada la aplicación se leen.
Compatibilidad de opciones con nombre con IConfigureNamedOptions
Opciones con nombre:
- son útiles cuando varias secciones de configuración se enlazan a las mismas propiedades.
- Distinguen mayúsculas de minúsculas.
Fíjese en el siguiente archivo appsettings.json :
{
"TopItem": {
"Month": {
"Name": "Green Widget",
"Model": "GW46"
},
"Year": {
"Name": "Orange Gadget",
"Model": "OG35"
}
}
}
En lugar de crear dos clases para enlazar TopItem:Month y TopItem:Year, se usará la clase siguiente para cada sección:
public class TopItemSettings
{
public const string Month = "Month";
public const string Year = "Year";
public string Name { get; set; }
public string Model { get; set; }
}
El siguiente código configura las opciones con nombre:
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();
}
En el código siguiente se muestran las opciones con nombre:
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 las opciones son instancias con nombre. Las instancias de IConfigureOptions<TOptions> se usan para seleccionar como destino la instancia de Options.DefaultName, que es string.Empty. IConfigureNamedOptions<TOptions> también implementa IConfigureOptions<TOptions>. La implementación predeterminada de IOptionsFactory<TOptions> tiene lógica para usar cada una de forma adecuada. La opción con nombre null se usa para seleccionar como destino todas las instancias con nombre, en lugar de una instancia con nombre determinada. ConfigureAll y PostConfigureAll usan esta convención.
API OptionsBuilder
OptionsBuilder<TOptions> se usa para configurar instancias TOptions. OptionsBuilder simplifica la creación de opciones con nombre, ya que es un único parámetro para la llamada AddOptions<TOptions>(string optionsName) inicial en lugar de aparecer en todas las llamadas posteriores. La validación de opciones y las sobrecargas ConfigureOptions que aceptan las dependencias de servicio solo están disponibles mediante OptionsBuilder.
OptionsBuilder se usa en la sección Opciones de validación.
Vea Uso de AddOptions para configurar un repositorio personalizado para obtener información sobre cómo agregar un repositorio personalizado.
Uso de servicios de DI para configurar opciones
Hay dos formas de acceder a los servicios desde la inserción de dependencias durante la configuración de opciones:
Si se pasa un delegado de configuración a Configure en OptionsBuilder<TOptions>.
OptionsBuilder<TOptions>proporciona sobrecargas de Configure que permiten usar hasta cinco servicios para configurar las opciones:services.AddOptions<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));Si se crea un tipo que implementa IConfigureOptions<TOptions> o IConfigureNamedOptions<TOptions>, y se registra como un servicio.
Se recomienda pasar un delegado de configuración a Configure, ya que la creación de un servicio es más complicada. La creación de un tipo es equivalente a lo que el marco hace cuando se llama a Configure. La llamada a Configure registra una interfaz IConfigureNamedOptions<TOptions> genérica y transitoria, con un constructor que acepta los tipos de servicio genéricos especificados.
Opciones de validación
Opciones de validación permite que se validen los valores de opción.
Fíjese en el siguiente archivo appsettings.json :
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
La siguiente clase se enlaza a la sección de configuración "MyConfig" y aplica un par de reglas DataAnnotations:
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; }
}
El código siguiente:
- Llama a AddOptions para obtener un elemento OptionsBuilder<TOptions> que enlaza a la clase
MyConfigOptions. - Llama a ValidateDataAnnotations para habilitar la validación con
DataAnnotations.
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();
}
El método de extensión ValidateDataAnnotations se define en el paquete NuGet Microsoft.Extensions.Options.DataAnnotations. En el caso de las aplicaciones web que usan el SDK de Microsoft.NET.Sdk.Web, se hace referencia implícita a este paquete desde el marco compartido.
En el código siguiente se muestran los valores de configuración o los errores de validación:
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);
}
El código siguiente aplica una regla de validación más compleja mediante un 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 la validación compleja
La siguiente clase 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 el código de validación fuera de StartUp y dentro de una clase.
Con el código anterior, la validación se habilita en Startup.ConfigureServices con el código siguiente:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyConfigOptions>(Configuration.GetSection(
MyConfigOptions.MyConfig));
services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions
<MyConfigOptions>, MyConfigValidation>());
services.AddControllersWithViews();
}
Configuración posterior de las opciones
Establezca la configuración posterior con IPostConfigureOptions<TOptions>. La configuración posterior se ejecuta una vez completada toda la configuración de IConfigureOptions<TOptions>:
services.PostConfigure<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
PostConfigure está disponible para configurar posteriormente las opciones con nombre:
services.PostConfigure<MyOptions>("named_options_1", myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Use PostConfigureAll para configurar posteriormente todas las instancias de configuración:
services.PostConfigureAll<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Acceso a opciones durante el inicio
IOptions<TOptions> y IOptionsMonitor<TOptions> puede usarse en Startup.Configure, ya que los servicios se compilan antes de que se ejecute el método Configure.
public void Configure(IApplicationBuilder app,
IOptionsMonitor<MyOptions> optionsAccessor)
{
var option1 = optionsAccessor.CurrentValue.Option1;
}
No use IOptions<TOptions> o IOptionsMonitor<TOptions> en Startup.ConfigureServices. Puede que exista un estado incoherente de opciones debido al orden de los registros de servicio.
Paquete NuGet Options.ConfigurationExtensions
Se hace referencia implícita al paquete Microsoft.Extensions.Options.ConfigurationExtensions en las aplicaciones de ASP.NET Core.
El patrón de opciones usa clases para representar grupos de configuraciones relacionadas. Cuando los valores de configuración están aislados por escenario en clases independientes, la aplicación se ajusta a dos principios de ingeniería de software importantes:
- El principio de segregación de interfaz (ISP) o encapsulación: los escenarios (clases) que dependen de valores de configuración dependen únicamente de los valores de configuración que usen.
- Separación de intereses: los valores de configuración para distintos elementos de la aplicación no son dependientes entre sí ni están emparejados.
Las opciones también proporcionan un mecanismo para validar los datos de configuración. Para obtener más información, consulte la sección Opciones de validación.
Vea o descargue el código de ejemplo (cómo descargarlo)
Requisitos previos
Haga referencia al metapaquete Microsoft.AspNetCore.App o agregue una referencia de paquete al paquete Microsoft.Extensions.Options.ConfigurationExtensions.
Interfaces de opciones
IOptionsMonitor<TOptions> se usa para recuperar las opciones y administrar las notificaciones de las opciones para instancias de TOptions. IOptionsMonitor<TOptions> admite las siguientes situaciones:
- Notificaciones de cambios
- Opciones con nombre
- Configuración que se puede recargar
- Invalidación de opciones de selección (IOptionsMonitorCache<TOptions>)
Los escenarios posteriores a la configuración le permiten establecer o cambiar las opciones después de que finalice toda la configuración de IConfigureOptions<TOptions>.
IOptionsFactory<TOptions> es responsable de crear nuevas instancias de opciones. Tiene un solo método Create. La implementación predeterminada toma todas las instancias registradas de IConfigureOptions<TOptions> y IPostConfigureOptions<TOptions>, y establece todas las configuraciones primero, seguidas de las configuraciones posteriores. Distingue entre IConfigureNamedOptions<TOptions> y IConfigureOptions<TOptions>, y solo llama a la interfaz adecuada.
IOptionsMonitorCache<TOptions> se usa por IOptionsMonitor<TOptions> para almacenar en caché las instancias de TOptions. IOptionsMonitorCache<TOptions> invalida instancias de opciones en la supervisión para que se pueda volver a calcular el valor (TryRemove). Los valores se pueden introducir manualmente y mediante TryAdd. Se usa el método Clear cuando todas las instancias con nombre se deben volver a crear a petición.
IOptionsSnapshot<TOptions> es útil en escenarios donde se deben volver a calcular las opciones en cada solicitud. Para obtener más información, consulte la sección Volver a cargar los datos de configuración con IOptionsSnapshot.
IOptions<TOptions> puede utilizarse para admitir las opciones. Sin embargo, IOptions<TOptions> no es compatible con los escenarios anteriores de IOptionsMonitor<TOptions>. Aún puede usar IOptions<TOptions> en marcos y bibliotecas existentes que ya usan la interfaz IOptions<TOptions> y no requieren los escenarios proporcionados por IOptionsMonitor<TOptions>.
Configuración de opciones generales
La configuración de opciones generales se muestra en el ejemplo 1 en la aplicación de ejemplo.
Una clase de opciones debe ser no abstracta con un constructor público sin parámetros. La siguiente clase, MyOptions, tiene dos propiedades: Option1 y Option2. Configurar los valores predeterminados es opcional, pero el constructor de clases en el ejemplo siguiente establece el valor predeterminado de Option1. Option2 tiene un valor predeterminado que se establece al inicializar la propiedad directamente (Models/MyOptions.cs):
public class MyOptions
{
public MyOptions()
{
// Set default value.
Option1 = "value1_from_ctor";
}
public string Option1 { get; set; }
public int Option2 { get; set; } = 5;
}
La clase MyOptions se agrega al contenedor de servicios con Configure y se enlaza a la configuración:
// Example #1: General configuration
// Register the Configuration instance which MyOptions binds against.
services.Configure<MyOptions>(Configuration);
El siguiente modelo de página usa la inserción de dependencias de constructor con IOptionsMonitor<TOptions> para acceder a la configuración (Pages/Index.cshtml.cs):
private readonly MyOptions _options;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #1: Simple options
var option1 = _options.Option1;
var option2 = _options.Option2;
SimpleOptions = $"option1 = {option1}, option2 = {option2}";
El archivo appsettings.json del ejemplo especifica valores para option1 y option2:
{
"option1": "value1_from_json",
"option2": -1,
"subsection": {
"suboption1": "subvalue1_from_json",
"suboption2": 200
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
Cuando se ejecuta la aplicación, el método OnGet del modelo de página devuelve una cadena que muestra los valores de la clase de opción:
option1 = value1_from_json, option2 = -1
Nota
Al usar una instancia de ConfigurationBuilder personalizada para cargar las opciones de configuración desde un archivo de configuración, confirme que la ruta de acceso base esté configurada correctamente:
var configBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true);
var config = configBuilder.Build();
services.Configure<MyOptions>(config);
Al cargar las opciones de configuración desde el archivo de configuración a través de CreateDefaultBuilder, no es necesario establecer de forma explícita la ruta de acceso base.
Configurar opciones simples con un delegado
La configuración de opciones simples con un delegado se muestra como ejemplo 2 en la aplicación de ejemplo.
Use un delegado para establecer los valores de opciones. La aplicación de ejemplo usa la clase MyOptionsWithDelegateConfig (Models/MyOptionsWithDelegateConfig.cs):
public class MyOptionsWithDelegateConfig
{
public MyOptionsWithDelegateConfig()
{
// Set default value.
Option1 = "value1_from_ctor";
}
public string Option1 { get; set; }
public int Option2 { get; set; } = 5;
}
En el código siguiente, un segundo servicio IConfigureOptions<TOptions> se agrega al contenedor de servicios. Usa un delegado para configurar el enlace con MyOptionsWithDelegateConfig:
// Example #2: Options bound and configured by a delegate
services.Configure<MyOptionsWithDelegateConfig>(myOptions =>
{
myOptions.Option1 = "value1_configured_by_delegate";
myOptions.Option2 = 500;
});
Index.cshtml.cs:
private readonly MyOptionsWithDelegateConfig _optionsWithDelegateConfig;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #2: Options configured by delegate
var delegate_config_option1 = _optionsWithDelegateConfig.Option1;
var delegate_config_option2 = _optionsWithDelegateConfig.Option2;
SimpleOptionsWithDelegateConfig =
$"delegate_option1 = {delegate_config_option1}, " +
$"delegate_option2 = {delegate_config_option2}";
Puede agregar varios proveedores de configuración. Los proveedores de configuración están disponibles en paquetes de NuGet y se aplican en el orden en que están registrados. Para obtener más información, vea Configuración en ASP.NET Core.
Cada llamada a Configure agrega un servicio IConfigureOptions<TOptions> al contenedor de servicios. En el ejemplo anterior, los valores de Option1 y Option2 se especifican en appsettings.json , pero los valores de Option1 y Option2 se reemplazan por el delegado configurado.
Cuando se habilita más de un servicio de configuración, la última fuente de configuración especificada gana y establece el valor de configuración. Cuando se ejecuta la aplicación, el método OnGet del modelo de página devuelve una cadena que muestra los valores de la clase de opción:
delegate_option1 = value1_configured_by_delegate, delegate_option2 = 500
Configuración de subopciones
La configuración de subopciones se muestra en el ejemplo 3 en la aplicación de ejemplo.
Las aplicaciones deben crear clases de opciones que pertenezcan a grupos específicos de escenarios (clases) en la aplicación. Los elementos de la aplicación que requieran valores de configuración deben acceder solamente a los valores de configuración que usen.
Al enlazar opciones para la configuración, cada propiedad en el tipo de opciones se enlaza a una clave de configuración del formulario property[:sub-property:]. Por ejemplo, la propiedad MyOptions.Option1 se enlaza a la clave Option1, que se lee desde la propiedad option1 en appsettings.json .
En el código siguiente, se agrega un tercer servicio IConfigureOptions<TOptions> al contenedor de servicios. Enlaza MySubOptions a la sección subsection del archivo appsettings.json :
// Example #3: Suboptions
// Bind options using a sub-section of the appsettings.json file.
services.Configure<MySubOptions>(Configuration.GetSection("subsection"));
El método GetSection requiere el espacio de nombres Microsoft.Extensions.Configuration.
El archivo appsettings.json del ejemplo define un miembro subsection con las claves para suboption1 y suboption2:
{
"option1": "value1_from_json",
"option2": -1,
"subsection": {
"suboption1": "subvalue1_from_json",
"suboption2": 200
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
La clase MySubOptions define propiedades, SubOption1 y SubOption2, para mantener los valores de opciones (Models/MySubOptions.cs):
public class MySubOptions
{
public MySubOptions()
{
// Set default values.
SubOption1 = "value1_from_ctor";
SubOption2 = 5;
}
public string SubOption1 { get; set; }
public int SubOption2 { get; set; }
}
El método OnGet del modelo de página devuelve una cadena con los valores de opciones (Pages/Index.cshtml.cs):
private readonly MySubOptions _subOptions;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #3: Suboptions
var subOption1 = _subOptions.SubOption1;
var subOption2 = _subOptions.SubOption2;
SubOptions = $"subOption1 = {subOption1}, subOption2 = {subOption2}";
Cuando se ejecuta la aplicación, el método OnGet devuelve una cadena que muestra los valores de clase de subopciones:
subOption1 = subvalue1_from_json, subOption2 = 200
Inserción de opciones
La inserción de opciones se muestra en el ejemplo 4 en la aplicación de ejemplo.
Inserte IOptionsMonitor<TOptions> en:
- Una página de Razor o una vista de MVC con la directiva
@injectde Razor. - Un modelo de página o vista.
En el ejemplo siguiente de la aplicación de ejemplo, inserte IOptionsMonitor<TOptions> en un modelo de página (Pages/Index.cshtml.cs):
private readonly MyOptions _options;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #4: Bind options directly to the page
MyOptions = _options;
La aplicación de ejemplo muestra cómo insertar IOptionsMonitor<MyOptions> con una directiva @inject:
@page
@model IndexModel
@using Microsoft.Extensions.Options
@inject IOptionsMonitor<MyOptions> OptionsAccessor
@{
ViewData["Title"] = "Options Sample";
}
<h1>@ViewData["Title"]</h1>
Cuando se ejecuta la aplicación, se muestran los valores de opciones en la página representada:

Volver a cargar los datos de configuración con IOptionsSnapshot
El procedimiento de volver a cargar los datos de configuración con IOptionsSnapshot<TOptions> se muestra en el ejemplo 5 en la aplicación de ejemplo.
Al usar IOptionsSnapshot<TOptions>, cuando se accede a las opciones y se las almacena en caché durante la vigencia de la solicitud, se calculan una vez por solicitud.
La diferencia entre IOptionsMonitor y IOptionsSnapshot es que:
IOptionsMonitores un servicio singleton que recupera los valores de las opciones actuales en cualquier momento, lo que resulta especialmente útil en las dependencias singleton.IOptionsSnapshotes un servicio con ámbito y proporciona una instantánea de las opciones en el momento en que se construye el objetoIOptionsSnapshot<T>. Las instantáneas de opciones están diseñadas para usarlas con dependencias transitorias y con ámbito.
En el ejemplo siguiente se muestra cómo se crea un nuevo IOptionsSnapshot<TOptions> después de cambiar appsettings.json (Pages/Index.cshtml.cs). Varias solicitudes al servidor devuelven valores constantes proporcionados por el archivo appsettings.json hasta que se modifique el archivo y vuelva a cargarse la configuración.
private readonly MyOptions _snapshotOptions;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #5: Snapshot options
var snapshotOption1 = _snapshotOptions.Option1;
var snapshotOption2 = _snapshotOptions.Option2;
SnapshotOptions =
$"snapshot option1 = {snapshotOption1}, " +
$"snapshot option2 = {snapshotOption2}";
En la siguiente imagen se muestran los valores option1 y option2 iniciales cargados desde el archivo appsettings.json :
snapshot option1 = value1_from_json, snapshot option2 = -1
Cambie los valores del archivo appsettings.json a value1_from_json UPDATED y 200. Guarde el archivo appsettings.json. Actualice el explorador para ver qué valores de opciones se han actualizado:
snapshot option1 = value1_from_json UPDATED, snapshot option2 = 200
Compatibilidad de opciones con nombre con IConfigureNamedOptions
La compatibilidad de opciones con nombre con IConfigureNamedOptions<TOptions> se muestra en el ejemplo 6 de la aplicación de ejemplo.
La compatibilidad con las opciones con nombre permite a la aplicación distinguir entre las configuraciones de opciones con nombre. En la aplicación de ejemplo, las opciones con nombre se declaran con OptionsServiceCollectionExtensions.Configure, que llama al método de extensión ConfigureNamedOptions<TOptions>.Configure. Las opciones con nombre distinguen mayúsculas de minúsculas.
// Example #6: Named options (named_options_1)
// Register the ConfigurationBuilder instance which MyOptions binds against.
// Specify that the options loaded from configuration are named
// "named_options_1".
services.Configure<MyOptions>("named_options_1", Configuration);
// Example #6: Named options (named_options_2)
// Specify that the options loaded from the MyOptions class are named
// "named_options_2".
// Use a delegate to configure option values.
services.Configure<MyOptions>("named_options_2", myOptions =>
{
myOptions.Option1 = "named_options_2_value1_from_action";
});
La aplicación de ejemplo accede a las opciones con nombre con Get (Pages/Index.cshtml.cs):
private readonly MyOptions _named_options_1;
private readonly MyOptions _named_options_2;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #6: Named options
var named_options_1 =
$"named_options_1: option1 = {_named_options_1.Option1}, " +
$"option2 = {_named_options_1.Option2}";
var named_options_2 =
$"named_options_2: option1 = {_named_options_2.Option1}, " +
$"option2 = {_named_options_2.Option2}";
NamedOptions = $"{named_options_1} {named_options_2}";
Al ejecutar la aplicación de ejemplo, se devuelven las opciones con nombre:
named_options_1: option1 = value1_from_json, option2 = -1
named_options_2: option1 = named_options_2_value1_from_action, option2 = 5
Se proporcionan valores de named_options_1 a partir de la configuración, que se cargan desde el archivo appsettings.json . Los valores de named_options_2 los proporciona:
- El delegado
named_options_2enConfigureServicesparaOption1. - El valor predeterminado para
Option2proporcionado por la claseMyOptions.
Configurar todas las opciones con el método ConfigureAll
Configure todas las instancias de opciones con el método ConfigureAll. El siguiente código configura Option1 para todas las instancias de configuración con un valor común. Agregue manualmente este código al método Startup.ConfigureServices:
services.ConfigureAll<MyOptions>(myOptions =>
{
myOptions.Option1 = "ConfigureAll replacement value";
});
Al ejecutar la aplicación de ejemplo después de agregar el código se produce el siguiente resultado:
named_options_1: option1 = ConfigureAll replacement value, option2 = -1
named_options_2: option1 = ConfigureAll replacement value, option2 = 5
Nota
Todas las opciones son instancias con nombre. Las instancias de IConfigureOptions<TOptions> existentes se usan para seleccionar como destino la instancia de Options.DefaultName, que es string.Empty. IConfigureNamedOptions<TOptions> también implementa IConfigureOptions<TOptions>. La implementación predeterminada de IOptionsFactory<TOptions> tiene lógica para usar cada una de forma adecuada. La opción con nombre null se usa para seleccionar como destino todas las instancias con nombre, en lugar de una instancia con nombre determinada (ConfigureAll y PostConfigureAll usan esta convención).
API OptionsBuilder
OptionsBuilder<TOptions> se usa para configurar instancias TOptions. OptionsBuilder simplifica la creación de opciones con nombre, ya que es un único parámetro para la llamada AddOptions<TOptions>(string optionsName) inicial en lugar de aparecer en todas las llamadas posteriores. La validación de opciones y las sobrecargas ConfigureOptions que aceptan las dependencias de servicio solo están disponibles mediante OptionsBuilder.
// Options.DefaultName = "" is used.
services.AddOptions<MyOptions>().Configure(o => o.Property = "default");
services.AddOptions<MyOptions>("optionalName")
.Configure(o => o.Property = "named");
Uso de servicios de DI para configurar opciones
Hay dos formas de acceder a otros servicios desde la inserción de dependencias durante la configuración de opciones:
Si se pasa un delegado de configuración a Configure en OptionsBuilder<TOptions>. OptionsBuilder<TOptions> proporciona sobrecargas de Configure que permiten usar hasta cinco servicios para configurar las opciones:
services.AddOptions<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));Si se crea un tipo propio que implementa IConfigureOptions<TOptions> o IConfigureNamedOptions<TOptions>, y se registrar como un servicio.
Se recomienda pasar un delegado de configuración a Configure, ya que la creación de un servicio es más complicada. La creación de un tipo propio es equivalente a lo que el marco hace de forma automática cuando se usa Configure. La llamada a Configure registra una interfaz IConfigureNamedOptions<TOptions> genérica y transitoria, con un constructor que acepta los tipos de servicio genéricos especificados.
Opciones de validación
Las opciones de validación permiten validar las opciones cuando se configuran. Llame a Validate con un método de validación que devuelve true si las opciones son válidas y false si no lo son:
// Registration
services.AddOptions<MyOptions>("optionalOptionsName")
.Configure(o => { }) // Configure the options
.Validate(o => YourValidationShouldReturnTrueIfValid(o),
"custom error");
// Consumption
var monitor = services.BuildServiceProvider()
.GetService<IOptionsMonitor<MyOptions>>();
try
{
var options = monitor.Get("optionalOptionsName");
}
catch (OptionsValidationException e)
{
// e.OptionsName returns "optionalOptionsName"
// e.OptionsType returns typeof(MyOptions)
// e.Failures returns a list of errors, which would contain
// "custom error"
}
El ejemplo anterior establece la instancia de opciones con nombre en optionalOptionsName. La instancia predeterminada es Options.DefaultName.
La validación se ejecuta cuando se crea la instancia de opciones. Una instancia de opciones pasa seguro la validación la primera vez que se accede.
Importante
La validación de opciones no protege contra las modificaciones de opciones después de crearse la instancia de opciones. Por ejemplo, las opciones IOptionsSnapshot se crean y validan una vez por solicitud al obtenerse acceso a estas por primera vez. Las opciones IOptionsSnapshot no se validarán de nuevo en intentos de acceso posteriores para la misma solicitud.
El método Validate acepta una expresión Func<TOptions, bool>. Para personalizar completamente la validación, implemente IValidateOptions<TOptions>, que permite:
- Validación de varios tipos de opciones:
class ValidateTwo : IValidateOptions<Option1>, IValidationOptions<Option2> - Validación que depende de otro tipo de opción:
public DependsOnAnotherOptionValidator(IOptionsMonitor<AnotherOption> options)
IValidateOptions valida:
- Una instancia de opciones con nombre específica.
- Todas las opciones cuando
nameesnull.
Devuelve ValidateOptionsResult de la implementación de la interfaz:
public interface IValidateOptions<TOptions> where TOptions : class
{
ValidateOptionsResult Validate(string name, TOptions options);
}
La validación basada en la anotación de datos está disponible en el paquete Microsoft.Extensions.Options.DataAnnotations llamando al método ValidateDataAnnotations en OptionsBuilder<TOptions>. Microsoft.Extensions.Options.DataAnnotations se incluye en el metapaquete Microsoft.AspNetCore.App.
using Microsoft.Extensions.DependencyInjection;
private class AnnotatedOptions
{
[Required]
public string Required { get; set; }
[StringLength(5, ErrorMessage = "Too long.")]
public string StringLength { get; set; }
[Range(-5, 5, ErrorMessage = "Out of range.")]
public int IntRange { get; set; }
}
[Fact]
public void CanValidateDataAnnotations()
{
var services = new ServiceCollection();
services.AddOptions<AnnotatedOptions>()
.Configure(o =>
{
o.StringLength = "111111";
o.IntRange = 10;
o.Custom = "nowhere";
})
.ValidateDataAnnotations();
var sp = services.BuildServiceProvider();
var error = Assert.Throws<OptionsValidationException>(() =>
sp.GetRequiredService<IOptionsMonitor<AnnotatedOptions>>().CurrentValue);
ValidateFailure<AnnotatedOptions>(error, Options.DefaultName, 1,
"DataAnnotation validation failed for members Required " +
"with the error 'The Required field is required.'.",
"DataAnnotation validation failed for members StringLength " +
"with the error 'Too long.'.",
"DataAnnotation validation failed for members IntRange " +
"with the error 'Out of range.'.");
}
La validación diligente (con respuesta rápida a errores en el inicio) se está teniendo en cuenta de cara a una versión futura.
Configuración posterior de las opciones
Establezca la configuración posterior con IPostConfigureOptions<TOptions>. La configuración posterior se ejecuta una vez completada toda la configuración de IConfigureOptions<TOptions>:
services.PostConfigure<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
PostConfigure está disponible para configurar posteriormente las opciones con nombre:
services.PostConfigure<MyOptions>("named_options_1", myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Use PostConfigureAll para configurar posteriormente todas las instancias de configuración:
services.PostConfigureAll<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Acceso a opciones durante el inicio
IOptions<TOptions> y IOptionsMonitor<TOptions> puede usarse en Startup.Configure, ya que los servicios se compilan antes de que se ejecute el método Configure.
public void Configure(IApplicationBuilder app, IOptionsMonitor<MyOptions> optionsAccessor)
{
var option1 = optionsAccessor.CurrentValue.Option1;
}
No use IOptions<TOptions> o IOptionsMonitor<TOptions> en Startup.ConfigureServices. Puede que exista un estado incoherente de opciones debido al orden de los registros de servicio.
El patrón de opciones usa clases para representar grupos de configuraciones relacionadas. Cuando los valores de configuración están aislados por escenario en clases independientes, la aplicación se ajusta a dos principios de ingeniería de software importantes:
- El principio de segregación de interfaz (ISP) o encapsulación: los escenarios (clases) que dependen de valores de configuración dependen únicamente de los valores de configuración que usen.
- Separación de intereses: los valores de configuración para distintos elementos de la aplicación no son dependientes entre sí ni están emparejados.
Las opciones también proporcionan un mecanismo para validar los datos de configuración. Para obtener más información, consulte la sección Opciones de validación.
Vea o descargue el código de ejemplo (cómo descargarlo)
Requisitos previos
Haga referencia al metapaquete Microsoft.AspNetCore.App o agregue una referencia de paquete al paquete Microsoft.Extensions.Options.ConfigurationExtensions.
Interfaces de opciones
IOptionsMonitor<TOptions> se usa para recuperar las opciones y administrar las notificaciones de las opciones para instancias de TOptions. IOptionsMonitor<TOptions> admite las siguientes situaciones:
- Notificaciones de cambios
- Opciones con nombre
- Configuración que se puede recargar
- Invalidación de opciones de selección (IOptionsMonitorCache<TOptions>)
Los escenarios posteriores a la configuración le permiten establecer o cambiar las opciones después de que finalice toda la configuración de IConfigureOptions<TOptions>.
IOptionsFactory<TOptions> es responsable de crear nuevas instancias de opciones. Tiene un solo método Create. La implementación predeterminada toma todas las instancias registradas de IConfigureOptions<TOptions> y IPostConfigureOptions<TOptions>, y establece todas las configuraciones primero, seguidas de las configuraciones posteriores. Distingue entre IConfigureNamedOptions<TOptions> y IConfigureOptions<TOptions>, y solo llama a la interfaz adecuada.
IOptionsMonitorCache<TOptions> se usa por IOptionsMonitor<TOptions> para almacenar en caché las instancias de TOptions. IOptionsMonitorCache<TOptions> invalida instancias de opciones en la supervisión para que se pueda volver a calcular el valor (TryRemove). Los valores se pueden introducir manualmente y mediante TryAdd. Se usa el método Clear cuando todas las instancias con nombre se deben volver a crear a petición.
IOptionsSnapshot<TOptions> es útil en escenarios donde se deben volver a calcular las opciones en cada solicitud. Para obtener más información, consulte la sección Volver a cargar los datos de configuración con IOptionsSnapshot.
IOptions<TOptions> puede utilizarse para admitir las opciones. Sin embargo, IOptions<TOptions> no es compatible con los escenarios anteriores de IOptionsMonitor<TOptions>. Aún puede usar IOptions<TOptions> en marcos y bibliotecas existentes que ya usan la interfaz IOptions<TOptions> y no requieren los escenarios proporcionados por IOptionsMonitor<TOptions>.
Configuración de opciones generales
La configuración de opciones generales se muestra en el ejemplo 1 en la aplicación de ejemplo.
Una clase de opciones debe ser no abstracta con un constructor público sin parámetros. La siguiente clase, MyOptions, tiene dos propiedades: Option1 y Option2. Configurar los valores predeterminados es opcional, pero el constructor de clases en el ejemplo siguiente establece el valor predeterminado de Option1. Option2 tiene un valor predeterminado que se establece al inicializar la propiedad directamente (Models/MyOptions.cs):
public class MyOptions
{
public MyOptions()
{
// Set default value.
Option1 = "value1_from_ctor";
}
public string Option1 { get; set; }
public int Option2 { get; set; } = 5;
}
La clase MyOptions se agrega al contenedor de servicios con Configure y se enlaza a la configuración:
// Example #1: General configuration
// Register the Configuration instance which MyOptions binds against.
services.Configure<MyOptions>(Configuration);
El siguiente modelo de página usa la inserción de dependencias de constructor con IOptionsMonitor<TOptions> para acceder a la configuración (Pages/Index.cshtml.cs):
private readonly MyOptions _options;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #1: Simple options
var option1 = _options.Option1;
var option2 = _options.Option2;
SimpleOptions = $"option1 = {option1}, option2 = {option2}";
El archivo appsettings.json del ejemplo especifica valores para option1 y option2:
{
"option1": "value1_from_json",
"option2": -1,
"subsection": {
"suboption1": "subvalue1_from_json",
"suboption2": 200
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
Cuando se ejecuta la aplicación, el método OnGet del modelo de página devuelve una cadena que muestra los valores de la clase de opción:
option1 = value1_from_json, option2 = -1
Nota
Al usar una instancia de ConfigurationBuilder personalizada para cargar las opciones de configuración desde un archivo de configuración, confirme que la ruta de acceso base esté configurada correctamente:
var configBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true);
var config = configBuilder.Build();
services.Configure<MyOptions>(config);
Al cargar las opciones de configuración desde el archivo de configuración a través de CreateDefaultBuilder, no es necesario establecer de forma explícita la ruta de acceso base.
Configurar opciones simples con un delegado
La configuración de opciones simples con un delegado se muestra como ejemplo 2 en la aplicación de ejemplo.
Use un delegado para establecer los valores de opciones. La aplicación de ejemplo usa la clase MyOptionsWithDelegateConfig (Models/MyOptionsWithDelegateConfig.cs):
public class MyOptionsWithDelegateConfig
{
public MyOptionsWithDelegateConfig()
{
// Set default value.
Option1 = "value1_from_ctor";
}
public string Option1 { get; set; }
public int Option2 { get; set; } = 5;
}
En el código siguiente, un segundo servicio IConfigureOptions<TOptions> se agrega al contenedor de servicios. Usa un delegado para configurar el enlace con MyOptionsWithDelegateConfig:
// Example #2: Options bound and configured by a delegate
services.Configure<MyOptionsWithDelegateConfig>(myOptions =>
{
myOptions.Option1 = "value1_configured_by_delegate";
myOptions.Option2 = 500;
});
Index.cshtml.cs:
private readonly MyOptionsWithDelegateConfig _optionsWithDelegateConfig;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #2: Options configured by delegate
var delegate_config_option1 = _optionsWithDelegateConfig.Option1;
var delegate_config_option2 = _optionsWithDelegateConfig.Option2;
SimpleOptionsWithDelegateConfig =
$"delegate_option1 = {delegate_config_option1}, " +
$"delegate_option2 = {delegate_config_option2}";
Puede agregar varios proveedores de configuración. Los proveedores de configuración están disponibles en paquetes de NuGet y se aplican en el orden en que están registrados. Para obtener más información, vea Configuración en ASP.NET Core.
Cada llamada a Configure agrega un servicio IConfigureOptions<TOptions> al contenedor de servicios. En el ejemplo anterior, los valores de Option1 y Option2 se especifican en appsettings.json , pero los valores de Option1 y Option2 se reemplazan por el delegado configurado.
Cuando se habilita más de un servicio de configuración, la última fuente de configuración especificada gana y establece el valor de configuración. Cuando se ejecuta la aplicación, el método OnGet del modelo de página devuelve una cadena que muestra los valores de la clase de opción:
delegate_option1 = value1_configured_by_delegate, delegate_option2 = 500
Configuración de subopciones
La configuración de subopciones se muestra en el ejemplo 3 en la aplicación de ejemplo.
Las aplicaciones deben crear clases de opciones que pertenezcan a grupos específicos de escenarios (clases) en la aplicación. Los elementos de la aplicación que requieran valores de configuración deben acceder solamente a los valores de configuración que usen.
Al enlazar opciones para la configuración, cada propiedad en el tipo de opciones se enlaza a una clave de configuración del formulario property[:sub-property:]. Por ejemplo, la propiedad MyOptions.Option1 se enlaza a la clave Option1, que se lee desde la propiedad option1 en appsettings.json .
En el código siguiente, se agrega un tercer servicio IConfigureOptions<TOptions> al contenedor de servicios. Enlaza MySubOptions a la sección subsection del archivo appsettings.json :
// Example #3: Suboptions
// Bind options using a sub-section of the appsettings.json file.
services.Configure<MySubOptions>(Configuration.GetSection("subsection"));
El método GetSection requiere el espacio de nombres Microsoft.Extensions.Configuration.
El archivo appsettings.json del ejemplo define un miembro subsection con las claves para suboption1 y suboption2:
{
"option1": "value1_from_json",
"option2": -1,
"subsection": {
"suboption1": "subvalue1_from_json",
"suboption2": 200
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
La clase MySubOptions define propiedades, SubOption1 y SubOption2, para mantener los valores de opciones (Models/MySubOptions.cs):
public class MySubOptions
{
public MySubOptions()
{
// Set default values.
SubOption1 = "value1_from_ctor";
SubOption2 = 5;
}
public string SubOption1 { get; set; }
public int SubOption2 { get; set; }
}
El método OnGet del modelo de página devuelve una cadena con los valores de opciones (Pages/Index.cshtml.cs):
private readonly MySubOptions _subOptions;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #3: Suboptions
var subOption1 = _subOptions.SubOption1;
var subOption2 = _subOptions.SubOption2;
SubOptions = $"subOption1 = {subOption1}, subOption2 = {subOption2}";
Cuando se ejecuta la aplicación, el método OnGet devuelve una cadena que muestra los valores de clase de subopciones:
subOption1 = subvalue1_from_json, subOption2 = 200
Opciones proporcionadas por un modelo de vista o con inserción de vista directa
Las opciones proporcionadas por un modelo de vista o de inserción de vista directa se muestran en el ejemplo 4 en la aplicación de ejemplo.
Las opciones se pueden suministrar en un modelo de vista o insertando IOptionsMonitor<TOptions> directamente en una vista (Pages/Index.cshtml.cs):
private readonly MyOptions _options;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #4: Bind options directly to the page
MyOptions = _options;
La aplicación de ejemplo muestra cómo insertar IOptionsMonitor<MyOptions> con una directiva @inject:
@page
@model IndexModel
@using Microsoft.Extensions.Options
@inject IOptionsMonitor<MyOptions> OptionsAccessor
@{
ViewData["Title"] = "Options Sample";
}
<h1>@ViewData["Title"]</h1>
Cuando se ejecuta la aplicación, se muestran los valores de opciones en la página representada:

Volver a cargar los datos de configuración con IOptionsSnapshot
El procedimiento de volver a cargar los datos de configuración con IOptionsSnapshot<TOptions> se muestra en el ejemplo 5 en la aplicación de ejemplo.
IOptionsSnapshot<TOptions> admite volver a cargar opciones con la mínima sobrecarga de procesamiento.
Cuando se accede a las opciones y se las almacena en caché durante la vigencia de la solicitud, se calculan una vez por solicitud.
En el ejemplo siguiente se muestra cómo se crea un nuevo IOptionsSnapshot<TOptions> después de cambiar appsettings.json (Pages/Index.cshtml.cs). Varias solicitudes al servidor devuelven valores constantes proporcionados por el archivo appsettings.json hasta que se modifique el archivo y vuelva a cargarse la configuración.
private readonly MyOptions _snapshotOptions;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #5: Snapshot options
var snapshotOption1 = _snapshotOptions.Option1;
var snapshotOption2 = _snapshotOptions.Option2;
SnapshotOptions =
$"snapshot option1 = {snapshotOption1}, " +
$"snapshot option2 = {snapshotOption2}";
En la siguiente imagen se muestran los valores option1 y option2 iniciales cargados desde el archivo appsettings.json :
snapshot option1 = value1_from_json, snapshot option2 = -1
Cambie los valores del archivo appsettings.json a value1_from_json UPDATED y 200. Guarde el archivo appsettings.json. Actualice el explorador para ver qué valores de opciones se han actualizado:
snapshot option1 = value1_from_json UPDATED, snapshot option2 = 200
Compatibilidad de opciones con nombre con IConfigureNamedOptions
La compatibilidad de opciones con nombre con IConfigureNamedOptions<TOptions> se muestra en el ejemplo 6 de la aplicación de ejemplo.
La compatibilidad con las opciones con nombre permite a la aplicación distinguir entre las configuraciones de opciones con nombre. En la aplicación de ejemplo, las opciones con nombre se declaran con OptionsServiceCollectionExtensions.Configure, que llama al método de extensión ConfigureNamedOptions<TOptions>.Configure. Las opciones con nombre distinguen mayúsculas de minúsculas.
// Example #6: Named options (named_options_1)
// Register the ConfigurationBuilder instance which MyOptions binds against.
// Specify that the options loaded from configuration are named
// "named_options_1".
services.Configure<MyOptions>("named_options_1", Configuration);
// Example #6: Named options (named_options_2)
// Specify that the options loaded from the MyOptions class are named
// "named_options_2".
// Use a delegate to configure option values.
services.Configure<MyOptions>("named_options_2", myOptions =>
{
myOptions.Option1 = "named_options_2_value1_from_action";
});
La aplicación de ejemplo accede a las opciones con nombre con Get (Pages/Index.cshtml.cs):
private readonly MyOptions _named_options_1;
private readonly MyOptions _named_options_2;
public IndexModel(
IOptionsMonitor<MyOptions> optionsAccessor,
IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig,
IOptionsMonitor<MySubOptions> subOptionsAccessor,
IOptionsSnapshot<MyOptions> snapshotOptionsAccessor,
IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
_options = optionsAccessor.CurrentValue;
_optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
_subOptions = subOptionsAccessor.CurrentValue;
_snapshotOptions = snapshotOptionsAccessor.Value;
_named_options_1 = namedOptionsAccessor.Get("named_options_1");
_named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #6: Named options
var named_options_1 =
$"named_options_1: option1 = {_named_options_1.Option1}, " +
$"option2 = {_named_options_1.Option2}";
var named_options_2 =
$"named_options_2: option1 = {_named_options_2.Option1}, " +
$"option2 = {_named_options_2.Option2}";
NamedOptions = $"{named_options_1} {named_options_2}";
Al ejecutar la aplicación de ejemplo, se devuelven las opciones con nombre:
named_options_1: option1 = value1_from_json, option2 = -1
named_options_2: option1 = named_options_2_value1_from_action, option2 = 5
Se proporcionan valores de named_options_1 a partir de la configuración, que se cargan desde el archivo appsettings.json . Los valores de named_options_2 los proporciona:
- El delegado
named_options_2enConfigureServicesparaOption1. - El valor predeterminado para
Option2proporcionado por la claseMyOptions.
Configurar todas las opciones con el método ConfigureAll
Configure todas las instancias de opciones con el método ConfigureAll. El siguiente código configura Option1 para todas las instancias de configuración con un valor común. Agregue manualmente este código al método Startup.ConfigureServices:
services.ConfigureAll<MyOptions>(myOptions =>
{
myOptions.Option1 = "ConfigureAll replacement value";
});
Al ejecutar la aplicación de ejemplo después de agregar el código se produce el siguiente resultado:
named_options_1: option1 = ConfigureAll replacement value, option2 = -1
named_options_2: option1 = ConfigureAll replacement value, option2 = 5
Nota
Todas las opciones son instancias con nombre. Las instancias de IConfigureOptions<TOptions> existentes se usan para seleccionar como destino la instancia de Options.DefaultName, que es string.Empty. IConfigureNamedOptions<TOptions> también implementa IConfigureOptions<TOptions>. La implementación predeterminada de IOptionsFactory<TOptions> tiene lógica para usar cada una de forma adecuada. La opción con nombre null se usa para seleccionar como destino todas las instancias con nombre, en lugar de una instancia con nombre determinada (ConfigureAll y PostConfigureAll usan esta convención).
API OptionsBuilder
OptionsBuilder<TOptions> se usa para configurar instancias TOptions. OptionsBuilder simplifica la creación de opciones con nombre, ya que es un único parámetro para la llamada AddOptions<TOptions>(string optionsName) inicial en lugar de aparecer en todas las llamadas posteriores. La validación de opciones y las sobrecargas ConfigureOptions que aceptan las dependencias de servicio solo están disponibles mediante OptionsBuilder.
// Options.DefaultName = "" is used.
services.AddOptions<MyOptions>().Configure(o => o.Property = "default");
services.AddOptions<MyOptions>("optionalName")
.Configure(o => o.Property = "named");
Uso de servicios de DI para configurar opciones
Hay dos formas de acceder a otros servicios desde la inserción de dependencias durante la configuración de opciones:
Si se pasa un delegado de configuración a Configure en OptionsBuilder<TOptions>. OptionsBuilder<TOptions> proporciona sobrecargas de Configure que permiten usar hasta cinco servicios para configurar las opciones:
services.AddOptions<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));Si se crea un tipo propio que implementa IConfigureOptions<TOptions> o IConfigureNamedOptions<TOptions>, y se registrar como un servicio.
Se recomienda pasar un delegado de configuración a Configure, ya que la creación de un servicio es más complicada. La creación de un tipo propio es equivalente a lo que el marco hace de forma automática cuando se usa Configure. La llamada a Configure registra una interfaz IConfigureNamedOptions<TOptions> genérica y transitoria, con un constructor que acepta los tipos de servicio genéricos especificados.
Configuración posterior de las opciones
Establezca la configuración posterior con IPostConfigureOptions<TOptions>. La configuración posterior se ejecuta una vez completada toda la configuración de IConfigureOptions<TOptions>:
services.PostConfigure<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
PostConfigure está disponible para configurar posteriormente las opciones con nombre:
services.PostConfigure<MyOptions>("named_options_1", myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Use PostConfigureAll para configurar posteriormente todas las instancias de configuración:
services.PostConfigureAll<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Acceso a opciones durante el inicio
IOptions<TOptions> y IOptionsMonitor<TOptions> puede usarse en Startup.Configure, ya que los servicios se compilan antes de que se ejecute el método Configure.
public void Configure(IApplicationBuilder app, IOptionsMonitor<MyOptions> optionsAccessor)
{
var option1 = optionsAccessor.CurrentValue.Option1;
}
No use IOptions<TOptions> o IOptionsMonitor<TOptions> en Startup.ConfigureServices. Puede que exista un estado incoherente de opciones debido al orden de los registros de servicio.