ASP.NET CoreのConfigureServices内のインスタンスを解決する方法


100

スタートアップIOptions<AppSettings>ConfigureServicesメソッドからのインスタンスを解決することは可能ですか?通常はを使用IServiceProviderしてインスタンスを初期化できますが、サービスを登録するときのこの段階ではインスタンスはありません。

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

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

回答:


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これにはパッケージが必要です。


でいくつかのオプションをバインドする必要がある場合はConfigureServicesBindメソッドを使用することもできます。

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

この機能は、Microsoft.Extensions.Configuration.Binderパッケージを通じて利用できます。


アプリケーションの別の部分でこのサービスを解決する必要がある場合はどうなりますか?すべてがConfigureServices()で完了していないと思いますか?
Ray

1
@Rayを使用すると、コンストラクター注入などのデフォルトの依存性注入メカニズムを使用できます。この質問は、特にConfigureServicesメソッド内のサービスの解決に関するものです。
Henk Mollema 2017年

@pcdevそうするとNULLになり、インスタンスを解決しようとします。最初にサービスを追加する必要があります。
IngoB 2018年

@IngoBええ、申し訳ありませんが、そのコメントは正しくなかったため削除する必要があります-そのコメントを書いたときに何が起こっているのか正しく理解できませんでした。以前にリンクした回答を参照しください。これは更新してからです。さらに調査を行った結果、理解が深まりました。
pcdev 2018年

14
これは、サービスを追加するメソッドに実装ファクトリのオーバーロード(ここでは例)がない場合に便利ですがBuildServiceProvider、アプリケーションコードで使用するとConfigureServices、シングルトンサービスの追加のコピーが生成されるなどの警告が発生します。作成した。ここでの Ehsan Mirsaeediの回答は、このような場合に最も理想的なソリューションです。
ネオ

65

他のサービスに依存するクラスをインスタンス化する最良の方法は、IServiceProviderを提供するAdd 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

@Jpsyあなたは無関係なものを混ぜます。AddCors、AddAuthenticationなどは、さまざまな基盤となるミドルウェアを接続するために登録エソッドの下で呼び出すヘルパーです。AddTransient、AddSingleton、AddScopedは3つの登録です(3つの一般的に使用されるライフタイム)
Fab

これはすべてのケースをカバーするわけではありません。解決策については私の答えを参照しください。
イアンケンプ

6

ASP.NET Coreのすべてのバージョンでこれを実現する最も簡単で最も正しい方法は、IConfigureOptions<TOptions>インターフェイスを実装することです。これは.NET Core 1.0から存在しますが、Just Work™の仕組みについて知っている人はほとんどいないようです。

例として、アプリケーションの他のサービスの1つに依存するカスタムモデルバリデーターを追加するとします。最初は不可能のようです-に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)デリゲートで行っていた設定はすべてConfigureServicesIConfigureOptions<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プレフィックスを付けてこの質問をグーグル検索してここに来ましたが、残念ながら特定のトピックは見つかりませんでした。ですから、この問題に苦しんでいるこの質問にも来るだろう他の人が答えを見つけることができると思います。
単純に
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.