是否可以从 Startup 中的 ConfigureServices
方法解析 IOptions<AppSettings>
的实例? The documentation explicitly says:
不要在 Startup.ConfigureServices 中使用 IOptions
您可以使用 serviceCollection.BuildServiceProvider()
手动创建服务提供者,但这会导致警告:
从应用程序代码调用“BuildServiceProvider”会导致创建单例服务的附加副本。考虑替代方案,例如依赖注入服务作为“配置”的参数。
我怎样才能做到这一点?
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettings>(
configuration.GetConfigurationSection(nameof(AppSettings)));
// How can I resolve IOptions<AppSettings> here?
}
如果您需要使用服务提供者手动解析服务,您可以使用此 AddSingleton/AddScoped/AddTransient
重载:
// Works for AddScoped and AddTransient as well
services.AddSingleton<IBarService>(sp =>
{
var fooService = sp.GetRequiredService<IFooService>();
return new BarService(fooService);
}
如果您真的想要,您可以使用 IServiceCollection
上的 BuildServiceProvider()
方法构建一个中间服务提供者:
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>>();
}
为此,您需要 Microsoft.Extensions.DependencyInjection
软件包。
但是,请注意,这会导致多个服务提供者实例,而这又可能导致多个单例实例。
如果只需要在 ConfigureServices
中绑定一些选项,也可以使用 Bind
方法:
var appSettings = new AppSettings();
configuration.GetSection(nameof(AppSettings)).Bind(appSettings);
此功能可通过 Microsoft.Extensions.Configuration.Binder
包获得。
实例化依赖于其他服务的类的最佳方法是使用为您提供 IServiceProvider 的 AddXXX 重载。这样您就不需要实例化中间服务提供者。
以下示例展示了如何在 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;
});
告诉您手动构建 IServiceProvider
以获取 IOptions<T>
实例的所有其他答案都是危险,因为它们是错误(至少在 ASP.NET Core 中) 3.0)!实际上,如果您今天使用这些答案,您将收到以下编译器警告:
从应用程序代码调用“BuildServiceProvider”会导致创建单例服务的附加副本。考虑替代方案,例如依赖注入服务作为“配置”的参数。
正确的方法是实现这一点,在所有版本的 ASP.NET Core 中都可以安全可靠地工作,实现自 .NET Core 1.0 以来就存在的 IConfigureOptions<TOptions>
接口 - 但似乎到目前为止知道 how it makes things Just Work™ 的人太少。
例如,您想要添加一个自定义模型验证器,该验证器依赖于您的应用程序的其他服务之一。最初似乎不可能 - 因为您无权访问 IServiceProvider
,所以无法解决 IMyServiceDependency
:
public class MyModelValidatorProvider : IModelValidatorProvider
{
public MyModelValidatorProvider(IMyServiceDependency dependency)
{
...
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.ModelValidatorProviders.Add(new MyModelValidatorProvider(??????));
});
}
但是 IConfigureOptions<TOptions>
的“魔力”让它变得如此简单:
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>();
}
本质上,您在 ConfigureServices
的 Add***(***Options)
委托中所做的任何设置现在都移至 IConfigureOptions<TOptions>
类的 Configure
方法。然后,您以与注册任何其他服务相同的方式注册选项,然后就可以走了!
如需更多详细信息以及有关其如何在幕后工作的信息,请参阅I refer you to the always-excellent Andrew Lock。
MvcOptions
,但您也以正常方式配置它。您的方式是“添加”到现有配置,还是“替换”它?
您是否正在寻找类似以下的内容?你可以看看我在代码中的注释:
// this call would new-up `AppSettings` type
services.Configure<AppSettings>(appSettings =>
{
// bind the newed-up type with the data from the configuration section
ConfigurationBinder.Bind(appSettings, Configuration.GetConfigurationSection(nameof(AppSettings)));
// modify these settings if you want to
});
// your updated app settings should be available through DI now
想帮助其他看起来相同但也在使用 Autofac 的人。
如果您想获得 ILifetimeScope(即当前范围的容器),您需要在 Configure(IApplicationBuilder app)
中调用 app.ApplicationServices.GetAutofacRoot()
方法,这将返回 ILifetimeScope 实例,您可以使用它来解析服务
public void Configure(IApplicationBuilder app)
{
//app middleware registrations
//...
//
ILifetimeScope autofacRoot = app.ApplicationServices.GetAutofacRoot();
var repository = autofacRoot.Resolve<IRepository>();
}
除了@henkmollema 回答,您还可以直接在 IConfiguration 上使用 Get 方法,例如
public void ConfigureServices(IServiceCollection services)
{
var iConf=configuration.GetSection(nameof(AppSettings));
services.Configure<AppSettings>(iConf);
// How can I resolve IOptions<AppSettings> here?
var opts=iConf.Get<AppSettings>();
}
注意:通过这种方式,您将直接获得 AppSettings 而不是 IOptions
在 MVC Core 3.1 或 .Net 5 中,您可以通过两行将 IOptions 传递给 Startup.cs
中的服务:
首先注册您的 IOptions
设置:
services.Configure<MySettings>(Configuration.GetSection("MySection"));
然后您的服务注册,传入 IOptions
:
services.AddSingleton<IMyService, MyService>(x => new MyService(x.GetService<IOptions<MySettings>>()));
不定期副业成功案例分享
ConfigureServices
方法中解析服务。ConfigureServices
)中使用BuildServiceProvider
,则会导致警告在正在创建的单例服务的附加副本中。 Ehsan Mirsaeedi 的回答 here 是此类案例的最理想解决方案。