ASP.NET Core MVCの依存関係注入フレームワークを使用して手動で型を解決する方法を教えてください。
コンテナの設定は簡単です。
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddTransient<ISomeService, SomeConcreteService>();
}
しかし、インジェクションを実行せずにISomeService
を解決するにはどうすればよいですか?たとえば、私はこれをしたいです。
ISomeService service = services.Resolve<ISomeService>();
IServiceCollection
にはそのようなメソッドはありません。
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
の使い方を追加してください)。
ランタイムは、Startup
、IHostingEnvironment
、IConfiguration
などのサービスを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)は アンチパターンとして知られている です。 (フレームワークやインフラストラクチャ層の)ユースケースはありますが、できるだけ避けるべきです。
インスタンスを手動で解決するには、 IServiceProvider
インターフェースを使用します。
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService, MyService>();
var serviceProvider = services.BuildServiceProvider();
var service = serviceProvider.GetService<IMyService>();
}
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";
テンプレートを使ってアプリケーションを生成すると、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();
}
登録している別の依存関係のコンストラクターに渡すために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/")
);
このようにAuthorizeAttributeのような属性に依存関係を注入することができます
var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));