ASPについてNET CoreのDI
8867 ワード
DIのいくつかの事
伝門マーティンおじさんの文章
依存注入(DI:Dependency Injection)とは?
依存注入(DI)はオブジェクト向けのソフトウェア設計モデルであり,主に開発者の松結合アプリケーションの開発を支援する.同時に、アプリケーションがユニットのテストとメンテナンスを容易にすることができます.
DIは実は1つの
で1つのオブジェクトにその依存を提供する過程です!どのようにもっとよく理解しますか?次は列を挙げて説明します!例えば、
class Client
は、サービスclass Service
が提供する機能を使用します.この場合、サービスはClientの依存であり、プログラムは次のように実現されます. var s = new Service();
var c = new Client(s);
明らかに私たちはサービスのオブジェクトを作成する職責を負わなければなりません.プログラムに強い結合の問題が発生しました.後で、もし需要が変化したら、サービスを交換しなければなりません.では、こちらのコードを修正しなければなりません.このようなプログラムは面明で、拡張性があり、柔軟性が悪いです.
DIを導入した後、
があるはずです.class Injector
と仮定します.DIの利点をよりよく説明するために、上記のコードはclass Client
に依存して IService
に再設定され、class Service
はIService
を実現しました.この時、私たちのプログラムの主流はどのようにサービスを作成するかに注目する必要はありません.この部分の職責をInjectorに委託することができます.私たちはInjectorに、私はIServiceが必要です.私に提供してください.プログラムの実現は以下の通りです.var s = Injector.Get(typeof(IService));
var c = new Client(s);
このようなメリットは明らかです.私たちは自分の核心業務の職責だけに注目して、どのように作成したのか、具体的にはどんな種類が実現したのか、自分で管理する必要はありません.権力は注入器に任せてください.
ポイント:実は上の過程で、私たちはもともと自分の一部の制御権を注入器に渡したことを発見したはずです.これは私たちがよく言っている
IOC
(Inversion of Control、制御反転)です.DIは実はIOC計の原則の一つの実現である.また、私たちが普段言っている観察者の黙認も、IOCの実現の一つであり、核心は一部の職責(非核心職責)を渡し、松結合の応用を構築することである.依存注入容器(DI Container)とは?
DI Containerは、IOC Containerとも呼ばれますが、実際にはフレームワーク(Framework)であり、依存を作成し、必要な他のオブジェクトに依存を自動的に注入し、依存のライフサイクルを管理するDIソリューションを提供しています.強力なサードパーティ製コンテナには、さまざまな機能が用意されており、コードをより楽しく引っ張ることができます.
常用サードパーティDI Container:
ASP .NET CoreのDI
ASP.NET Coreでは依存統合をサービス(services)と呼ぶため、DI ContainerはService Container,Aspとも呼ばれる.NET Coreは簡単な内蔵容器
IServiceProvider
を提供し、デフォルトではコンストラクタ注入constructor injection
をサポートし、私たちのほとんどの機能ニーズを満たしています.サービスは主に2種類に分けられます.
IApplicationBuilder
、IHostingEnvironment
...、詳しくはhttps://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1#framework-provided-services . DIコンテナを通じて私たちのサービスを自動的に注入するには、まずコンテナにサービスを登録しなければなりません(アプリケーションサービスを登録するだけで、フレームワークサービスはASP.NET Coreフレームワークに注入されています).
レジストリサービス
ILogインタフェースとその実装クラスがあると仮定し,DIコンテナに注入し,アプリケーションで使用する.
public interface ILog
{
void Info(string msg);
void Error(string err);
}
public class ConsoleLogger:ILog
{
public void Info(string msg)
{
Console.WriteLine(msg);
}
public void Error(string err)
{
Console.WriteLine(err);
}
}
その後、
Startup
クラスのConfigureServices()
メソッドに上記のサービスが登録され、ConfigureServices()
にはIServiceCollection
パラメータがあり、アプリケーションサービスを登録するために使用される.public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.Add(new ServiceDescriptor(typeof(ILog), typeof(ConsoleLogger), ServiceLifetime.Singleton));
}
}
クラス
ServiceDescriptor
は、サービスのタイプ、サービスの特定のインプリメンテーションがすでにサービスされているライフサイクル(Service Lifetime)を記述するために使用され、サービスILogのインプリメンテーションがConsoleLoggerであり、単一の例であることを指定した.通常、
IServiceCollection
拡張メソッドを使用してサービスを登録しています.services.AddSingleton();//
services.TryAddSingleton();
コンストラクタ注入(Contractor Injection)
関数を登録すると,アプリケーションクラスの構築関数に依存するサービスが含まれている場合,DIコンテナは依存を自動的に注入する.
public class HomeController : Controller
{
ILog _log;
public HomeController(ILog log)
{
_log = log;
}
public IActionResult Index()
{
_log.Info("Executing /home/index");
return View();
}
}
コントローラはILogサービスを必要とし、コンストラクション関数のパラメータにILogタイプを含めるだけでよい.他に何もする必要はなく、DIコンテナは自動的にILogのインスタンスを作成し、注入時に指定されたライフサイクルに基づいて、タイミングでこの実例を破棄する.
Actionメソッド注入(Method Injection)
ある方法でこのサービスを必要とする場合があります.この場合、スキームのパラメータに
[FromServices]
という特性をマークすることができ、コンテナはこの依存サービスのインスタンスを自動的に注入することができます.public IActionResult Index([FromServices] ILog log)
{
log.Info("Index method executing");
return View();
}
属性注入(Property Injection)
ASP.NET Coreがこのコンテナを持参してもサポートされていません.Autofacなどのサードパーティ製コンテナを使用する必要があります.
サービスのライフサイクル(Service Lifetime)
DIコンテナは、登録されたサービスのライフサイクルを管理し、指定されたライフサイクルに従ってサービスインスタンスを自動的に破棄します.ASP.NET Coreのサービスでは、次の3つのライフサイクル形式を構成できます.
//
services.Add(new ServiceDescriptor(typeof(ILog), typeof(ConsoleLogger), ServiceLifetime.Transient));
//
services.AddTransient();
//
services.Add(new ServiceDescriptor(typeof(ILog), typeof(ConsoleLogger), ServiceLifetime.Scoped));
//
services.AddScoped();
//
services.Add(new ServiceDescriptor(typeof(ILog), new ConsoleLogger()));
//
services.AddSingleton();
DI使用経験のまとめ
汎用型はどのように登録しますか?
オープン汎用(Open Generics)でサービスを登録します.上記のタイプが
ILog
に変更されたと仮定します.ConsoleLogger
です.では、次のように登録すればいいです.services.AddScoped(typeof(ILog<>), typeof(ConsoleLogger<>));
同じインタフェースタイプ注入2つの実装タイプはどうなりますか?
サービスを登録している場合、同じタイプの登録は複数回間違っていませんが、解析時に最後に登録したタイプのインスタンスが返されます.
public interface ILog
{
void Info(string msg);
}
public class Logger1 : ILog
{
public void Info(string msg)
{
}
}
public class Logger2 : ILog
{
public void Info(string msg)
{
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient();
services.AddTransient();
}
}
public class HomeController : Controller
{
ILog _log;
public HomeController(ILog log)
{
_log = log;//_log is Logger2
}
}
複数回の登録を回避するために、特定の実装が上書きされる問題を回避するために、
TryAddTransient
を使用するのが一般的です.この方法で登録すると、同じタイプが登録されていることが検出され、登録は行われません.public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.TryAddTransient();
services.TryAddTransient();
}
}
public class HomeController : Controller
{
ILog _log;
public HomeController(ILog log)
{
_log = log;//_log is Logger1
}
}
自分が手動でコンテナからサービスオブジェクトを取得したいのですが、どうすればいいですか?
ASP.NET CoreのDI Containerは
IServiceProvider
であり,このオブジェクトを取得してGetService
というメソッドを呼び出せばよい.RequestServices
がIServiceProvider
型である場合、 public IActionResult Index()
{
var services = this.HttpContext.RequestServices;
var log = (ILog)services.GetService(typeof(ILog));
log.Info("Index method executing");
return View();
}
IServiceProvider
public class MyAppService
{
private IServiceProvider _services;
public MyAppService(IServiceProvider services)
{
_services=services;
}
public void Test()
{
//
var log = (ILog)_services.GetService(typeof(ILog));
// , nuget Microsoft.Extensions.DependencyInjection.Abstractions
var log = _services.GetService();
}
}
締めくくり
ASP.NET Coreはすでに強力で、多くの実用的な機能を提供しています.Netの戦友たちは基本的な知識の備蓄の前提の下で、多く発掘してASPをまとめることができます.NET Core開発のベストプラクティスは、推進のために.NET Core生態建設は自分の力に貢献します!