web-dev-qa-db-ja.com

ASP.NET Core DIを使用したインスタンスの解決

ASP.NET Core MVCの依存関係注入フレームワークを使用して手動で型を解決する方法を教えてください。

コンテナの設定は簡単です。

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddTransient<ISomeService, SomeConcreteService>();
}

しかし、インジェクションを実行せずにISomeServiceを解決するにはどうすればよいですか?たとえば、私はこれをしたいです。

ISomeService service = services.Resolve<ISomeService>();

IServiceCollectionにはそのようなメソッドはありません。

190
Dave New

IServiceCollectionインターフェースはbuilding依存性注入コンテナに使用されます。完全にビルドされると、サービスの解決に使用できるIServiceProviderインスタンスに変換されます。IServiceProviderを任意のクラスに注入できます。IApplicationBuilderクラスとHttpContextクラスそれぞれApplicationServicesプロパティまたはRequestServicesプロパティを介して、サービスプロバイダにも提供できます。

IServiceProviderは、サービスを解決するためのGetService(Type type)メソッドを定義します。

var service = (IFooService)serviceProvider.GetService(typeof(IFooService));

serviceProvider.GetService<IFooService>()のような便利な拡張方法もいくつかあります。 (Microsoft.Extensions.DependencyInjectionの使い方を追加してください)。

スタートアップクラス内のサービスを解決する

依存関係を注入する

ランタイムは、StartupIHostingEnvironmentIConfigurationなどのサービスをIServiceProviderクラスのコンストラクタに挿入できます。このサービスプロバイダは、ホスティングレイヤによって構築されたインスタンスであり、アプリケーションを起動するためのサービスのみを含みます。

サービスはConfigure()メソッドで注入することもできます。 IApplicationBuilderパラメータの後に任意の数のパラメータを追加できます。ここでConfigureServices()メソッドに登録されているあなた自身のサービスを注入することもできます。それらはhostingservice providerではなくapplicationservice provider)から解決されます。

public void Configure(IApplicationBuilder app, IFooService fooService)
{
   // ...
}

ConfigureServices()メソッドはサービスを注入することを許可しません、しかしそれはIServiceCollection引数を受け入れるだけです。これは、アプリケーション依存性注入コンテナーを構成する方法です。ここでスタートアップのコンストラクタに注入されたサービスを使うことができます。例えば:

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    // Use Configuration here
}

依存関係を手動で解決する

手動でサービスを解決したい場合は、ランタイムにコンストラクタのIServiceProviderインスタンスを注入させるか、Configure()メソッドのApplicationServicesによって提供されるIApplicationBuilderを使用することができます。

public Startup(IServiceProvider serviceProvider)
{
    var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}

または

public void Configure(IApplicationBuilder app)
{
    var serviceProvider = app.ApplicationServices;
    var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}

ただし、ConfigureServices()メソッドでサービスを解決する必要がある場合は、別の方法が必要です。それまでに登録されたサービスを含むIServiceProviderインスタンスから中間のIServiceCollectionを構築できます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IFooService, FooService>();

    // Build the intermediate service provider
    var sp = services.BuildServiceProvider();
    var fooService = sp.GetService<IFooService>();
}

これには Microsoft.Extensions.DependencyInjection パッケージが必要です。

注意してください:
ConfigureServices()メソッド内のサービスは、実際には自分がconfigureアプリケーションサービス)になる場所であるため、通常は解決しないでください。場合によっては、いくつかのIOptions<MyOptions>インスタンスにアクセスする必要があります。 IConfigurationインスタンスの値をMyOptionsのインスタンスにバインドすることによって(これは基本的にoptionsフレームワークがすることです):

public void ConfigureServices(IServiceCollection services)
{
    var myOptions = new MyOptions();
    Configuration.GetSection("SomeSection").Bind(myOptions);
}

手動でサービスを解決すること(別名Service Locator)は アンチパターンとして知られている です。 (フレームワークやインフラストラクチャ層の)ユースケースはありますが、できるだけ避けるべきです。

311
Henk Mollema

インスタンスを手動で解決するには、 IServiceProvider インターフェースを使用します。

Startup.ConfigureServicesでの依存関係の解決

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();

    var serviceProvider = services.BuildServiceProvider();
    var service = serviceProvider.GetService<IMyService>();
}

Startup.Configureで依存関係を解決する

public void Configure(
    IApplicationBuilder application,
    IServiceProvider serviceProvider)
{
    // By type.
    var service1 = (MyService)serviceProvider.GetService(typeof(MyService));

    // Using extension method.
    var service2 = serviceProvider.GetService<MyService>();

    // ...
}

実行時注入サービスの使用

いくつかの型はメソッドのパラメータとして挿入できます。

public class Startup
{
    public Startup(
        IHostingEnvironment hostingEnvironment,
        ILoggerFactory loggerFactory)
    {
    }

    public void ConfigureServices(
        IServiceCollection services)
    {
    }

    public void Configure(
        IApplicationBuilder application,
        IHostingEnvironment hostingEnvironment,
        IServiceProvider serviceProvider,
        ILoggerFactory loggerfactory,
        IApplicationLifetime applicationLifetime)
    {
    }
}

コントローラアクションの依存関係の解決

[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";
66

テンプレートを使ってアプリケーションを生成すると、Startupクラスに次のようなものができます。

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddMvc();
}

その後、そこに依存関係を追加することができます。

services.AddTransient<ITestService, TestService>();

あなたがあなたのコントローラ上でITestServiceにアクセスしたいなら、あなたはコンストラクタ上でIServiceProviderを追加することができます、そしてそれは注入されます:

public HomeController(IServiceProvider serviceProvider)

その後、追加したサービスを解決できます。

var service = serviceProvider.GetService<ITestService>();

汎用バージョンを使用するには、名前空間を拡張子付きで含める必要があります。

using Microsoft.Extensions.DependencyInjection;

ITestService.cs

public interface ITestService
{
    int GenerateRandom();
}

TestService.cs

public class TestService : ITestService
{
    public int GenerateRandom()
    {
        return 4;
    }
}

Startup.cs(ConfigureServices)

public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(Configuration);
    services.AddMvc();

    services.AddTransient<ITestService, TestService>();
}

HomeController.cs

using Microsoft.Extensions.DependencyInjection;

namespace Core.Controllers
{
    public class HomeController : Controller
    {
        public HomeController(IServiceProvider serviceProvider)
        {
            var service = serviceProvider.GetService<ITestService>();
            int rnd = service.GenerateRandom();
        }
13
BrunoLM

登録している別の依存関係のコンストラクターに渡すために1つの依存関係を解決するだけでよい場合は、これを実行できます。

文字列とISomeServiceを取り入れたサービスがあるとしましょう。

public class AnotherService : IAnotherService
{
    public AnotherService(ISomeService someService, string serviceUrl)
    {
        ...
    }
}

Startup.cs内にこれを登録するときは、これを行う必要があります。

services.AddScoped<IAnotherService>(ctx => 
      new AnotherService(ctx.GetService<ISomeService>(), "https://someservice.com/")
);
1
raterus

このようにAuthorizeAttributeのような属性に依存関係を注入することができます

var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));
0
Bora Aydın