サーチ…
前書き
Aspnetコアは、主要なコア概念の1つとして、依存性注入を使用して構築されています。適合するコンテナの抽象概念を1つ導入して、組み込みのコンテナを選択したサードパーティのコンテナに置き換えることができます。
構文
-
IServiceCollection.Add(ServiceDescriptor item);
-
IServiceCollection.AddScoped(Type serviceType);
-
IServiceCollection.AddScoped(Type serviceType, Type implementationType);
-
IServiceCollection.AddScoped(Type serviceType, Func<IServiceProvider, object> implementationFactory);
-
IServiceCollection.AddScoped<TService>()
-
IServiceCollection.AddScoped<TService>(Func<IServiceProvider, TService> implementationFactory)
-
IServiceCollection.AddScoped<TService, TImplementation>()
-
IServiceCollection.AddScoped<TService, TImplementation>(Func<IServiceProvider, TImplementation> implementationFactory)
-
IServiceCollection.AddSingleton(Type serviceType);
-
IServiceCollection.AddSingleton(Type serviceType, Func<IServiceProvider, object> implementationFactory);
-
IServiceCollection.AddSingleton(Type serviceType, Type implementationType);
-
IServiceCollection.AddSingleton(Type serviceType, object implementationInstance);
-
IServiceCollection.AddSingleton<TService>()
-
IServiceCollection.AddSingleton<TService>(Func<IServiceProvider, TService> implementationFactory)
-
IServiceCollection.AddSingleton<TService>(TService implementationInstance)
-
IServiceCollection.AddSingleton<TService, TImplementation>()
-
IServiceCollection.AddSingleton<TService, TImplementation>(Func<IServiceProvider, TImplementation> implementationFactory)
-
IServiceCollection.AddTransient(Type serviceType);
-
IServiceCollection.AddTransient(Type serviceType, Func<IServiceProvider, object> implementationFactory);
-
IServiceCollection.AddTransient(Type serviceType, Type implementationType);
-
IServiceCollection.AddTransient<TService>()
-
IServiceCollection.AddTransient<TService>(Func<IServiceProvider, TService> implementationFactory)
-
IServiceCollection.AddTransient<TService, TImplementation>()
-
IServiceCollection.AddTransient<TService, TImplementation>(Func<IServiceProvider, TImplementation> implementationFactory)
-
IServiceProvider.GetService(Type serviceType)
-
IServiceProvider.GetService<T>()
-
IServiceProvider.GetServices(Type serviceType)
-
IServiceProvider.GetServices<T>()
備考
IServiceProvider
メソッドの汎用バリアントを使用するには、次の名前空間を含める必要があります。
using Microsoft.Extensions.DependencyInjection;
登録して手動で解決する
依存関係を記述する好ましい方法は、 Explicit Dependencies Principleに従うコンストラクタインジェクションを使用することです。
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.AddTransient<ITestService, TestService>();
}
HomeController.cs
using Microsoft.Extensions.DependencyInjection;
namespace Core.Controllers
{
public class HomeController : Controller
{
public HomeController(ITestService service)
{
int rnd = service.GenerateRandom();
}
}
}
依存関係の登録
Builtinコンテナには、組み込み機能のセットが付属しています:
ライフタイムコントロール
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddTransient<ITestService, TestService>();
// or
services.AddScoped<ITestService, TestService>();
// or
services.AddSingleton<ITestService, TestService>();
// or
services.AddSingleton<ITestService>(new TestService());
}
- AddTransient :解決されるたびに作成される
- AddScoped :リクエストごとに1回作成
- AddSingleton :アプリケーションごとに1回軽く作成
- AddSingleton(インスタンス) :アプリケーションごとに以前に作成されたインスタンスを提供します。
列挙可能な依存関係
列挙可能な依存関係を登録することもできます。
services.TryAddEnumerable(ServiceDescriptor.Transient<ITestService, TestServiceImpl1>());
services.TryAddEnumerable(ServiceDescriptor.Transient<ITestService, TestServiceImpl2>());
次のようにそれらを消費することができます:
public class HomeController : Controller
{
public HomeController(IEnumerable<ITestService> services)
{
// do something with services.
}
}
一般的な依存関係
一般的な依存関係を登録することもできます:
services.Add(ServiceDescriptor.Singleton(typeof(IKeyValueStore<>), typeof(KeyValueStore<>)));
そしてそれを次のように消費する:
public class HomeController : Controller
{
public HomeController(IKeyValueStore<UserSettings> userSettings)
{
// do something with services.
}
}
コントローラの依存関係を取得する
登録されると、Controllerコンストラクタにパラメータを追加することによって依存関係を取得できます。
// ...
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Core.Controllers
{
public class HomeController : Controller
{
public HomeController(ITestService service)
{
int rnd = service.GenerateRandom();
}
}
}
コントローラアクションへの依存性の注入
あまり知られていない組み込み機能は、 FromServicesAttribute
を使用したコントローラアクションインジェクションFromServicesAttribute
。
[HttpGet]
public async Task<IActionResult> GetAllAsync([FromServices]IProductService products)
{
return Ok(await products.GetAllAsync());
}
重要なことは、 [FromServices]
は一般的な "Property Injection"または "Method Injection"メカニズムとして使用できないことです。これはコントローラアクションまたはコントローラコンストラクタのメソッドパラメータでのみ使用できます(コンストラクタでは、ASP.NET Core DIシステムでコンストラクタインジェクションが既に使用されており、余分なマーカーは必要ありません)。
これは、コントローラ、コントローラアクションの外では使用できません 。また、ASP.NET Core MVCに固有のもので、 Microsoft.AspNetCore.Mvc.Core
アセンブリにも存在しMicrosoft.AspNetCore.Mvc.Core
。
この属性に関するASP.NET Core MVC GitHubのオリジナルの引用( [FromServices]をパラメータのみに適用する )
@rynowak:
@エロン:
プロパティの問題は、どのオブジェクトのどのプロパティにも適用できることが多くの人に見られるということです。
同意すると、ユーザーがこの機能の使用方法を混乱させていくつかの問題を投稿しました。 「[FromServices]は奇妙で、私はそれが好きではありません」と「[FromServices]」が私を混乱させました。それはトラップのようなもので、何年も前から何年も質問に答えているチームだと感じています。
[FromServices]がメソッドパラメータ上にあるという最も貴重なシナリオは、あなたがその場所で必要とするサービスのアクションだけです。
/ cc @ danroth27 - docsの変更点
現在の[FromServices]に恋している人には、プロパティ注入を行うことができるDIシステム(たとえば、Autofac)を調べることを強くお勧めします。
ノート:
.NETのコア依存性注入システムに登録されている任意のサービスが使用して、コントローラのアクション内に注入することができる
[FromServices]
属性を。最も関連性のあるケースは、単一のアクションメソッドでのみサービスが必要な場合で、コントローラーのコンストラクターを別の依存関係で混乱させたくない場合です。これは1回のみ使用されます。
Microsoft.AspNetCore.Mvc.Core
アセンブリに存在するため、ASP.NET Core MVC(純粋な.NET Frameworkまたは.NET Coreコンソールアプリケーション)の外部では使用できません。プロパティまたはメソッドの注入には、サードパーティのIoCコンテナ(Autofac、Unityなど)のいずれかを使用する必要があります。
オプションパターン/サービスへのオプションの挿入
Microsoftチームは、ASP.NET Coreを使用して、オプションパターンを導入しました。これにより強力な型指定オプションがあり、一度オプションをサービスに組み込むことができます。
最初に、私たちの設定を保持する強力な型付きクラスから始めます。
public class MySettings
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
appsettings.json
エントリ。
{
"mysettings" : {
"value1": "Hello",
"value2": "World"
}
}
次にStartupクラスで初期化します。これを行うには2つの方法があります
appsettings.json
"mysettings"セクションから直接ロードしてください。services.Configure<MySettings>(Configuration.GetSection("mysettings"));
手動で行う
services.Configure<MySettings>(new MySettings { Value1 = "Hello", Value2 = Configuration["mysettings:value2"] });
appsettings.json
各階層レベルは:
区切られてい:
。value2
はmysettings
オブジェクトのプロパティなので、mysettings:value2
をmysettings:value2
てアクセスしmysettings:value2
。
最後に、 IOptions<T>
インタフェースを使用して、オプションをサービスに注入できます
public class MyService : IMyService
{
private readonly MySettings settings;
public MyService(IOptions<MySettings> mysettings)
{
this.settings = mySettings.Value;
}
}
備考
場合IOptions<T>
注入、起動時に設定されていないIOptions<T>
の既定のインスタンス注入しますT
クラスを。
アプリケーション起動/データベースシード中にスコープ付きサービスを使用する
要求がなく、スコープされたサービスがないため、アプリケーションの起動時にスコープ付きサービスを解決することは困難です。
app.ApplicationServices.GetService<AppDbContext>()
によるアプリケーションの起動時にスコープされたサービスを解決すると、グローバルコンテナのスコープ内に作成されるため、問題が発生する可能性があります。アプリケーションの存続期間とともにシングルトンになり、 Cannot access a disposed object in ASP.NET Core when injecting DbContext
。
次のパターンは、最初に新しいスコープを作成してスコープされたサービスを解決した後、スコープされたコンテナーを破棄して作業を完了することで問題を解決します。
public Configure(IApplicationBuilder app)
{
// serviceProvider is app.ApplicationServices from Configure(IApplicationBuilder app) method
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var db = serviceScope.ServiceProvider.GetService<AppDbContext>();
if (await db.Database.EnsureCreatedAsync())
{
await SeedDatabase(db);
}
}
}
これはEntity Frameworkのコアチームがアプリケーション起動時にデータをシードする半公式の方法であり、 MusicStoreサンプルアプリケーションに反映されます。
Dependency Injectionを介してコントローラ、ViewComponents、およびTagHelpersを解決する
デフォルトでは、Controller、ViewComponents、TagHelpersは依存性注入コンテナを介して登録され解決されません。これは、AutoFacのようなサードパーティInversion of Control(IoC)コンテナを使用しているときにプロパティ注入を行うことができないという結果になります。
ASP.NET Core MVCでIoCを介してこれらのタイプを解決するには、 Startup.cs
次のStartup.cs
を追加する必要があります(GitHubの公式ControllersFromServiceサンプルから取得)
public void ConfigureServices(IServiceCollection services)
{
var builder = services
.AddMvc()
.ConfigureApplicationPartManager(manager => manager.ApplicationParts.Clear())
.AddApplicationPart(typeof(TimeScheduleController).GetTypeInfo().Assembly)
.ConfigureApplicationPartManager(manager =>
{
manager.ApplicationParts.Add(new TypesPart(
typeof(AnotherController),
typeof(ComponentFromServicesViewComponent),
typeof(InServicesTagHelper)));
manager.FeatureProviders.Add(new AssemblyMetadataReferenceFeatureProvider());
})
.AddControllersAsServices()
.AddViewComponentsAsServices()
.AddTagHelpersAsServices();
services.AddTransient<QueryValueService>();
services.AddTransient<ValueService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
Plain Dependency Injectionの例(Startup.csなし)
これは、 WebHostBuilder
を使用せずにMicrosoft.Extensions.DependencyInjectionの nugetパッケージを使用する方法を示しています(たとえば、 WebHostBuilder
を作成したいときなど)。
internal class Program
{
public static void Main(string[] args)
{
var services = new ServiceCollection(); //Creates the service registry
services.AddTransient<IMyInterface, MyClass>(); //Add registration of IMyInterface (should create an new instance of MyClass every time)
var serviceProvider = services.BuildServiceProvider(); //Build dependencies into an IOC container
var implementation = serviceProvider.GetService<IMyInterface>(); //Gets a dependency
//serviceProvider.GetService<ServiceDependingOnIMyInterface>(); //Would throw an error since ServiceDependingOnIMyInterface is not registered
var manualyInstaniate = new ServiceDependingOnIMyInterface(implementation);
services.AddTransient<ServiceDependingOnIMyInterface>();
var spWithService = services.BuildServiceProvider(); //Generaly its bad practise to rebuild the container because its heavey and promotes use of anti-pattern.
spWithService.GetService<ServiceDependingOnIMyInterface>(); //only now i can resolve
}
}
interface IMyInterface
{
}
class MyClass : IMyInterface
{
}
class ServiceDependingOnIMyInterface
{
private readonly IMyInterface _dependency;
public ServiceDependingOnIMyInterface(IMyInterface dependency)
{
_dependency = dependency;
}
}
Microsoft.Extensions.DependencyInjectionの内部動作
IServiceCollection
MicrosoftのDI nugetパッケージを使用してIOCコンテナを構築するには、まずIServiceCollection
作成を開始します。既に提供されているCollection: ServiceCollection
:
var services = new ServiceCollection();
このIServiceCollection
は、 IList<ServiceDescriptor>, ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable
次のメソッドはすべて、 ServiceDescriptor
インスタンスをリストに追加する拡張メソッドです。
services.AddTransient<Class>(); //add registration that is always recreated
services.AddSingleton<Class>(); // add registration that is only created once and then re-used
services.AddTransient<Abstract, Implementation>(); //specify implementation for interface
services.AddTransient<Interface>(serviceProvider=> new Class(serviceProvider.GetService<IDependency>())); //specify your own resolve function/ factory method.
services.AddMvc(); //extension method by the MVC nuget package, to add a whole bunch of registrations.
// etc..
//when not using an extension method:
services.Add(new ServiceDescriptor(typeof(Interface), typeof(Class)));
IServiceProvider
serviceproviderはすべての登録を「コンパイル」して、すぐに使用できるようにしservices.BuildServiceProvider()
これはservices.BuildServiceProvider()
で行うことができます。これは、基本的には拡張用のmehtodです:
var provider = new ServiceProvider( services, false); //false is if it should validate scopes
その背後には、 IServiceCollection
すべてのServiceDescriptor
がファクトリメソッドFunc<ServiceProvider, object>
にコンパイルされFunc<ServiceProvider, object>
ここで、objectは戻り値の型で、実装タイプのインスタンス、シングルトンまたは独自の定義済みファクトリメソッドです。
これらの登録はServiceTable
追加されますServiceTable
これは基本的にConcurrentDictionary
であり、キーはServiceType
あり、値は上で定義したFactoryメソッドです。
結果
これで、 ConcurrentDictionary<Type, Func<ServiceProvider, object>>
が作成され、サービスを作成するために同時に使用できます。これがどのように見えるかの基本的な例を示す。
var serviceProvider = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>();
var factoryMethod = serviceProvider[typeof(MyService)];
var myServiceInstance = factoryMethod(serviceProvider)
これはどのように動作するのではありません!
このConcurrentDictionary
は、 ServiceProvider
プロパティであるServiceTable
のプロパティです