C# 如何在ConfigureServices内解析IOptions实例?

C# 如何在ConfigureServices内解析IOptions实例?,c#,asp.net-core,inversion-of-control,asp.net-core-mvc,C#,Asp.net Core,Inversion Of Control,Asp.net Core Mvc,是否可以解析IOptions的实例: 不要在Startup.ConfigureServices中使用IOptions或ioptionmonitor。由于服务注册的顺序,可能存在不一致的选项状态 您可以使用serviceCollection.BuildServiceProvider()手动创建服务提供程序,但这会导致警告: 从应用程序代码调用“BuildServiceProvider”会导致创建一个额外的单例服务副本。考虑诸如依赖注入服务之类的选项来“配置”参数。 我怎样才能做到这一点 publi

是否可以解析
IOptions的实例:

不要在
Startup.ConfigureServices中使用
IOptions
ioptionmonitor
由于服务注册的顺序,可能存在不一致的选项状态

您可以使用
serviceCollection.BuildServiceProvider()
手动创建服务提供程序,但这会导致警告:

从应用程序代码调用“BuildServiceProvider”会导致创建一个额外的单例服务副本。考虑诸如依赖注入服务之类的选项来“配置”参数。 我怎样才能做到这一点

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<AppSettings>(
        configuration.GetConfigurationSection(nameof(AppSettings)));

    // How can I resolve IOptions<AppSettings> here?
}
public void配置服务(IServiceCollection服务)
{
服务。配置(
GetConfigurationSection(nameof(AppSettings));
//我如何解决这里的问题?
}

您是否正在寻找以下内容?您可以在代码中查看我的注释:

// 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
//此调用将更新'AppSettings'类型
配置(appSettings=>
{
//用配置部分的数据绑定新的up类型
Bind(appSettings,Configuration.GetConfigurationSection(nameof(appSettings)));
//如果需要,请修改这些设置
});
//您更新的应用程序设置现在应该可以通过DI获得

如果您需要使用服务提供商手动解析服务,您可以使用此
AddSingleton/AddScoped/AddTransient
重载:

// Works for AddScoped and AddTransient as well
services.AddSingleton<IBarService>(sp =>
{
    var fooService = sp.GetRequiredService<IFooService>();
    return new BarService(fooService);
}
为此,您需要Microsoft.Extensions.DependencyInjection

但是请注意,这会导致多个服务提供商实例,而这又可能导致多个单例实例


如果只需要绑定
ConfigureServices
中的一些选项,也可以使用
bind
方法:

var appSettings = new AppSettings();
configuration.GetSection(nameof(AppSettings)).Bind(appSettings);

此功能可通过Microsoft.Extensions.Configuration.Binder软件包获得。

实例化依赖于其他服务的类的最佳方法是使用添加XXX重载,该重载为您提供IServiceProvider。这样,您就不需要实例化中间服务提供者

以下示例显示如何在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;
});
services.AddSingleton(serviceProvider=>
{
var options=serviceProvider.GetService();
var foo=新foo(选项);
返回foo;
});
services.AddTransient(serviceProvider=>
{
var options=serviceProvider.GetService();
var bar=新的条形图(选项);
返回杆;
});

想帮助其他看起来一样但也使用Autofac的人

如果您想获取ILifetimeScope(即当前作用域的容器),则需要调用
Configure(IAApplicationBuilder app)
中的
app.ApplicationServices.GetAutofacRoot()
方法,这将返回可用于解析服务的ILifetimeScope实例

public void Configure(IApplicationBuilder app)
    {
        //app middleware registrations 
        //...
        //

        ILifetimeScope autofacRoot = app.ApplicationServices.GetAutofacRoot();
        var repository = autofacRoot.Resolve<IRepository>();
    }
public void配置(IApplicationBuilder应用程序)
{
//应用程序中间件注册
//...
//
ILifetimeScope autofacRoot=app.ApplicationServices.GetAutofacRoot();
var repository=autofacRoot.Resolve();
}

所有其他告诉您手动构建
iSeries Provider
以获取
IOptions
实例的答案都是危险的,因为它们是错误的(至少从ASP.NET Core 3.0开始)!事实上,如果您今天使用这些答案,您将得到以下编译器警告:

从应用程序代码调用“BuildServiceProvider”会导致创建一个额外的单例服务副本。考虑诸如依赖注入服务之类的选项来“配置”参数。 正确的方法是实现这一点,在ASP.NET Core的所有版本中都能安全可靠地工作,它是实现自.NET Core 1.0以来就存在的接口-但似乎知道的人太少了

例如,您希望添加一个自定义模型验证器,该验证器依赖于应用程序的其他服务之一。起初这似乎是不可能的-无法解决
IMyServiceDependency
,因为您无法访问
iSeries Provider

public class MyModelValidatorProvider : IModelValidatorProvider
{
    public MyModelValidatorProvider(IMyServiceDependency dependency)
    {
        ...
    }
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.ModelValidatorProviders.Add(new MyModelValidatorProvider(??????));
    });
}
但是
IConfigureOptions
的“魔力”让它变得如此简单:

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>();
}
公共类配置MVCOPIONS:IConfigureOptions
{
私有IMyServiceDependency_dependency;
公共mymvcopions(IMyServiceDependency)
=>_dependency=dependency;
公共无效配置(MVCOPIONS选项)
=>options.ModelValidatorProviders.Add(新的MyModelValidatorProvider(_依赖项));
}
public void配置服务(IServiceCollection服务)
{
services.AddControllers();
...
//或范围化或暂时性,视服务需要而定
services.AddSingleton();
}
本质上,您在
ConfigureServices
中的
Add***(***选项)
委托中所做的任何设置现在都被移动到
IConfigureOptions
类的
Configure
方法中。然后,您注册选项的方式与注册任何其他服务的方式相同,然后就可以开始了


有关更多详细信息以及如何在后台工作的信息,请参见。

如果需要在应用程序的另一部分中解决此服务,该怎么办?我确信这不是全部在ConfigureServices()中完成的,对吗?@Ray然后您可以使用默认的依赖项注入机制,例如构造函数注入。这个问题特别是关于在
ConfigureServices
方法中解析服务的问题。@pcdev这样做时会得到NULL,然后尝试解析实例。您必须首先添加服务,但在添加服务的方法没有
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>();
}