如何在ASP.NET Core中解析ConfigureServices中的实例

Muh*_*eed 67 c# inversion-of-control asp.net-core-mvc asp.net-core

是否可以IOptions<AppSettings>ConfigureServicesStartup中的方法解析实例?通常,您可以使用IServiceProvider初始化实例,但在注册服务时此阶段没有实例.

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

    // How can I resolve IOptions<AppSettings> here?
}
Run Code Online (Sandbox Code Playgroud)

Hen*_*ema 112

您可以使用以下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>>();
}
Run Code Online (Sandbox Code Playgroud)

你需要这个Microsoft.Extensions.DependencyInjection包.


在您只需要绑定某些选项的情况下ConfigureServices,您还可以使用以下Bind方法:

var appSettings = new AppSettings();
configuration.GetSection(nameof(AppSettings)).Bind(appSettings);
Run Code Online (Sandbox Code Playgroud)

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

  • 尽管在添加服务的方法没有实现工厂重载的情况下这可能很有用(例如[here](/sf/ask/4037767541/ -predictionenginepool-in-middleware-ml-net#comment103385403_57698799)),如果在诸如“ ConfigureServices”之类的应用程序代码中使用“ BuildServiceProvider”,则使用“ BuildServiceProvider”会导致警告,因为这会导致创建单例服务的其他副本。Ehsan Mirsaeedi的答案[here](/sf/answers/3939461921/)是针对此类情况的最理想解决方案。 (3认同)
  • 这个答案是错误的,因为如果有任何单例注册,它可能会导致创建多次单例。@ehsan-mirsaeedi 答案更好,应该是公认的答案。 (3认同)
  • @Ray 然后你可以使用默认的依赖注入机制,比如构造函数注入。这个问题特别是关于在 `ConfigureServices` 方法中解析服务。 (2认同)

Ian*_*emp 18

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

从应用程序代码调用“BuildServiceProvider”会导致创建一个额外的单例服务副本。考虑替代方案,例如将依赖注入服务作为“配置”的参数。

正确的方法是实现这一点,在所有版本的 ASP.NET Core 中安全可靠地工作,是实现IConfigureOptions<TOptions>自 .NET Core 1.0 以来就存在的接口 - 但似乎很少有人知道它是如何制作东西的工作™

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

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

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.ModelValidatorProviders.Add(new MyModelValidatorProvider(??????));
    });
}
Run Code Online (Sandbox Code Playgroud)

但它的“魔法”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>();
}
Run Code Online (Sandbox Code Playgroud)

本质上,您在Add***(***Options)委托中完成的任何设置ConfigureServices现在都移到了您的IConfigureOptions<TOptions>类的Configure方法中。然后,您以注册任何其他服务的相同方式注册选项,然后就可以了!

有关更多详细信息以及有关幕后工作原理的信息,我向您推荐始终出色的 Andrew Lock


Ehs*_*edi 17

实例化依赖于其他服务的类的最佳方法是使用为您提供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;
});
Run Code Online (Sandbox Code Playgroud)

  • 使用此解决方案而不是 .Net Core 3 或更高版本接受的答案! (26认同)
  • @Joshit我不太确定这是否是所有情况下已接受答案的可行替代品。IServiceProvider 可用于即 AddSingleton、AddScoped、AddTransient。但还有许多其他 Add 方法不提供此重载,即 AddCors、AddAuthentication、AddAuthorization。 (16认同)