¿Es posible resolver una instancia de IOptions<AppSettings> desde el método ConfigureServices en el inicio? La documentación dice explícitamente :
No use
IOptions<TOptions>oIOptionsMonitor<TOptions>enStartup.ConfigureServices.Puede existir un estado de opciones inconsistente debido al orden de los registros de servicio.
Puede crear manualmente un proveedor de servicios usando serviceCollection.BuildServiceProvider() pero esto da como resultado la advertencia:
Al llamar a 'BuildServiceProvider' desde el código de la aplicación, se crea una copia adicional de los servicios singleton. Considere alternativas como los servicios de inyección de dependencia como parámetros para 'Configurar'.
¿Cómo puedo conseguir esto?
public void ConfigureServices(IServiceCollection services) { services.Configure<AppSettings>( configuration.GetConfigurationSection(nameof(AppSettings))); // How can I resolve IOptions<AppSettings> here? }Si necesita resolver el servicio usando el proveedor de servicios manualmente, puede usar esta AddSingleton/AddScoped/AddTransient :
// Works for AddScoped and AddTransient as well services.AddSingleton<IBarService>(sp => { var fooService = sp.GetRequiredService<IFooService>(); return new BarService(fooService); } Si realmente lo desea, puede crear un proveedor de servicios intermedio utilizando el método BuildServiceProvider() en IServiceCollection :
public void ConfigureService(IServiceCollection services) { // Configure the services services.AddTransient<IFooService, FooServiceImpl>(); services.Configure<AppSettings>(configuration.GetSection(nameof(AppSettings))); // Build an intermediate service provider var sp = services.BuildServiceProvider(); // Resolve the services from the service provider var fooService = sp.GetService<IFooService>(); var options = sp.GetService<IOptions<AppSettings>>(); } Necesita el paquete Microsoft.Extensions.DependencyInjection para esto.
Sin embargo , tenga en cuenta que esto da como resultado múltiples instancias de proveedores de servicios que, a su vez, pueden generar múltiples instancias de singleton.
En el caso de que solo necesite vincular algunas opciones en ConfigureServices , también puede usar el método Bind :
var appSettings = new AppSettings(); configuration.GetSection(nameof(AppSettings)).Bind(appSettings); Esta funcionalidad está disponible a través del paquete Microsoft.Extensions.Configuration.Binder .
La mejor manera de crear instancias de clases que dependen de otros servicios es usar la sobrecarga Add XXX que le proporciona IServiceProvider . De esta forma, no es necesario crear una instancia de un proveedor de servicios intermedio.
Los siguientes ejemplos muestran cómo puede usar esta sobrecarga en los métodos AddSingleton/AddTransient .
services.AddSingleton(serviceProvider => { var options = serviceProvider.GetService<IOptions<AppSettings>>(); var foo = new Foo(options); return foo ; }); services.AddTransient(serviceProvider => { var options = serviceProvider.GetService<IOptions<AppSettings>>(); var bar = new Bar(options); return bar; });¡Todas las demás respuestas que le indican que cree manualmente un IServiceProvider para obtener una instancia de IOptions<T> son peligrosas porque son incorrectas (al menos a partir de ASP.NET Core 3.0)! De hecho, si usa esas respuestas hoy, recibirá la siguiente advertencia del compilador:
Al llamar a 'BuildServiceProvider' desde el código de la aplicación, se crea una copia adicional de los servicios singleton. Considere alternativas como los servicios de inyección de dependencia como parámetros para 'Configurar'.
La forma correcta de lograr esto, que funciona de manera segura y confiable en todas las versiones de ASP.NET Core , es implementar la interfaz IConfigureOptions<TOptions> que existe desde .NET Core 1.0, pero parece que muy poca gente sabe cómo hace que las cosas Just Work™ .
Como ejemplo, desea agregar un validador de modelo personalizado que dependa de uno de los otros servicios de su aplicación. Inicialmente parece imposible: no hay forma de resolver IMyServiceDependency porque no tiene acceso a un IServiceProvider :
public class MyModelValidatorProvider : IModelValidatorProvider { public MyModelValidatorProvider(IMyServiceDependency dependency) { ... } } public void ConfigureServices(IServiceCollection services) { services.AddControllers(options => { options.ModelValidatorProviders.Add(new MyModelValidatorProvider(??????)); }); } Pero la "magia" de IConfigureOptions<TOptions> lo hace tan fácil:
public class ConfigureMvcOptions : IConfigureOptions<MvcOptions> { private IMyServiceDependency _dependency; public MyMvcOptions(IMyServiceDependency dependency) => _dependency = dependency; public void Configure(MvcOptions options) => options.ModelValidatorProviders.Add(new MyModelValidatorProvider(_dependency)); } public void ConfigureServices(IServiceCollection services) { services.AddControllers(); ... // or scoped, or transient, as necessary for your service services.AddSingleton<IConfigureOptions<MvcOptions>, ConfigureMvcOptions>(); } Básicamente, cualquier configuración que hubiera realizado en los delegados Add***(***Options) en ConfigureServices ahora se mueve al método Configure de su clase IConfigureOptions<TOptions> . Luego, registra las opciones de la misma manera que registraría cualquier otro servicio, ¡y listo!
Para obtener más detalles, así como información sobre cómo funciona esto detrás de escena, lo remito al siempre excelente Andrew Lock .