如何在ASP.NET Core中解析ConfigureConfigs内部的实例


100

是否可以IOptions<AppSettings>从“ ConfigureServices启动”中的方法解析实例?通常,您可以IServiceProvider用来初始化实例,但是在注册服务时此阶段没有实例。

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

    // How can I resolve IOptions<AppSettings> here?
}

Answers:


151

您可以使用上的BuildServiceProvider()方法来构建服务提供商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>>();
}

您需要此Microsoft.Extensions.DependencyInjection包装。


如果您只需要在中绑定某些选项ConfigureServices,则还可以使用Bind方法:

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

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


如果您需要在应用程序的另一部分中解决该服务怎么办?我确定还不是全部在ConfigureServices()中完成吗?

1
@Ray然后可以使用默认的依赖项注入机制,例如构造函数注入。这个问题专门与解决ConfigureServices方法内部的服务有关。
Henk Mollema

@pcdev这样做时,您会得到NULL,然后尝试解析该实例。您必须先添加服务。
IngoB

@IngoB是的,很抱歉,该评论不正确,应将其删除-写此评论时,我无法正确理解发生了什么。请查看我以前链接到的答案,此后我已经对其进行了更新-我做了一些调查,现在更好地理解了。
pcdev

14
虽然在添加服务的方法没有实现工厂重载的情况下(例如,here)可能很有用,但是BuildServiceProvider如果在应用程序代码中使用,则使用会引起警告,例如,会导致ConfigureServices正在复制单例服务创建。伊赫桑Mirsaeedi的答案这里是这样的情况下最为理想的解决方案。

65

实例化依赖于其他服务的类的最佳方法是使用为您提供IServiceProviderAdd XXX重载。这样,您无需实例化中间服务提供商。

下面的示例演示如何在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;
});

16
使用此解决方案,而不是接受.Net Core 3或更高版本的答案!
Joshit

7
@Joshit我不确定在所有情况下这是否都可以替代公认的答案。IServiceProvider可用于即AddSingleton,AddScoped,AddTransient。但是还有许多其他Add方法不提供此重载,即AddCors,AddAuthentication,AddAuthorization。
Jpsy

1
@Jpsy您混淆了无关的事物。AddCors,AddAuthentication等是在注册方法下调用的助手,以连接各种底层中间件。AddTransient,AddSingleton,AddScoped是这三个注册(具有三个常用的生存期)
Fab

这并不涵盖所有情况。请参阅我的答案以获取解决方案。
伊恩·坎普

7

在所有版本的ASP.NET Core中,最简单,最正确的方法是实现IConfigureOptions<TOptions>接口。尽管自.NET Core 1.0以来一直存在,但似乎很少有人知道它如何使Just Work™成为现实。

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

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 MyMvcOptions : 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)
{
    // or scoped, or transient
    services.AddSingleton<IConfigureOptions<MvcOptions>, MyMvcOptions>();
    services.AddControllers();
}

本质上,您在Add***(***Options)委托中完成的所有设置ConfigureServices现在都将移至您IConfigureOptions<TOptions>班级的Configure方法中。然后,您可以使用注册其他任何服务的相同方式注册选项,然后离开!

有关更多详细信息以及有关如何在后台进行工作的信息,我将向您推荐始终出色的Andrew Locke


1

您是否正在寻找类似的东西?您可以在代码中查看我的注释:

// 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

-1

想要帮助其他看起来相同但也使用Autofac的人。

如果要获取ILifetimeScope(即当前范围的容器),则需要调用app.ApplicationServices.GetAutofacRoot()方法,Configure(IApplicationBuilder app)该方法将返回ILifetimeScope实例,可用于解析服务

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

        ILifetimeScope autofacRoot = app.ApplicationServices.GetAutofacRoot();
        var repository = autofacRoot.Resolve<IRepository>();
    }

1
这个答案对AutoFac来说太具体了,这不在此问题的范围内。
Pure.Krome

我是通过使用autofac前缀搜索此问题而来到这里的,但不幸的是没有找到任何特定的主题。因此,我希望也遇到这个问题并且也在这个问题上苦苦挣扎的其他人能够找到答案。
很好
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.