1.前言 面向對象設計(OOD)里有一個重要的思想就是依賴倒置原則(DIP),並由該原則牽引出依賴註入(DI)、控制反轉(IOC)及其容器等老生常談的概念,初學者很容易被這些概念搞暈(包括我在內),在學習Core依賴註入服務之前,下麵讓我們先瞭解下依賴倒置原則(DIP)、依賴註入(DI)、控制反轉( ...
1.前言
面向對象設計(OOD)里有一個重要的思想就是依賴倒置原則(DIP),並由該原則牽引出依賴註入(DI)、控制反轉(IOC)及其容器等老生常談的概念,初學者很容易被這些概念搞暈(包括我在內),在學習Core依賴註入服務之前,下麵讓我們先瞭解下依賴倒置原則(DIP)、依賴註入(DI)、控制反轉(IOC)等概念,然後再深入學習Core依賴註入服務。
2.依賴倒置原則(DIP)
高層模塊不依賴於低層模塊的實現,而低層模塊依賴於高層模塊定義的介面。通俗來講,就是高層模塊定義介面,低層模塊負責實現。
2.依賴註入(DI)
2.1依賴(D)
當一個類需要另一個類協作來完成工作的時候就產生了依賴。
示例1:
public class MyDependency { public MyDependency() { } public Task WriteMessage(string message) { Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}"); return Task.FromResult(0); } } public class IndexModel : PageModel { MyDependency _dependency = new MyDependency(); public void OnGet() { _dependency.WriteMessage("IndexModel.OnGet created this message."); } }
由上述代碼可以看到IndexModel 模塊輸出消息必須要實例化MyDependency模塊,也就是說IndexModel 模塊業務的實現必須依賴於MyDependency模塊,這就是依賴。
2.2 註入(I)
根據DIP設計原則:高層模塊不依賴於低層模塊的實現,而低層模塊依賴於高層模塊定義的介面,所以我們在這裡定義一個介面供高層模塊調用,底層模塊負責實現。
示例2:
public interface IMyDependency { Task WriteMessage(string message); } public class MyDependency: IMyDependency { public MyDependency() { } public Task WriteMessage(string message) { Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}"); return Task.FromResult(0); } } public class IndexModel : PageModel { IMyDependency _dependency = new MyDependency(); public void OnGet() { _dependency.WriteMessage("IndexModel.OnGet created this message."); } }
從上述代碼可以看到當我們調用IndexModel 模塊OnGetAsync方法的時候,是通過IMyDependency介面實例化MyDependency類去實現其方法內容的,這叫控制正轉。但是Master說,我們不應該創建MyDependency類,而是讓調用者給你傳遞,於是你通過構造函數讓外界把這兩個依賴給你。
示例3:
public interface IMyDependency { Task WriteMessage(string message); } public class MyDependency : IMyDependency { private readonly ILogger<MyDependency> _logger; public MyDependency(ILogger<MyDependency> logger) { _logger = logger; } public Task WriteMessage(string message) { _logger.LogInformation( "MyDependency.WriteMessage called. Message: {MESSAGE}", message); return Task.FromResult(0); } } public class IndexModel : PageModel { private readonly IMyDependency _dependency; public IndexModel(IMyDependency dependency) { _dependency = dependency; } public void OnGet() { _dependency.WriteMessage("IndexModel.OnGet created this message."); } }
從上述代碼可以看到把依賴的創建丟給第三方系統(例:Autofac,Unity容器),也叫控制反轉(IOC)容器。自己只負責使用,其它人丟給你依賴的這個過程理解為註入。也叫控制反轉(IOC)。註意,框架內部ILogger介面已註入,無需手動再重新註入。
2.3 IOC容器
IOC容器可以看作是負責統一管理依賴關係的地方。常見有Autofac,Unity。
容器只要負責兩件事情:
●綁定服務與實例之間的關係
●獲取實例,並對實例進行管理(創建與銷毀)
3.依賴倒置原則(DIP)與控制反轉(IOC)的區別
DIP是一種軟體設計原則,它僅僅告訴你高低層模塊之間應該如何依賴,但是它並沒有告訴我們如何解除相互依賴模塊的耦合。而IOC則是一種軟體設計模式,它告訴我們該如何解除模塊的耦合,它為相互依賴的組件提供抽象,將依賴(低層模塊)對象的獲得交給第三方系統(例:Autofac,Unity容器)來控制,即依賴對象不在被依賴模塊的類中直接通過new來獲取。
4.NET Core依賴註入(DI)服務
經過上面描述,大家應該應該對依賴倒置原則(DIP)、依賴註入(DI)、控制反轉(IOC)這幾個概念有一定瞭解對吧。在.NET Core中DI的核心分為兩個組件:IServiceCollection和 IServiceProvider。
●IServiceCollection負責註冊
●IServiceProvider負責提供實例
下麵讓我們來學習下NET Core是怎麼依賴註入(DI)服務。
第一步:使用介面來實現依賴反轉。定義 IMyDependency 服務。
public interface IMyDependency { Task WriteMessage(string message); }
第二步:定義IMyDependency 服務的實現類MyDependency。
public class MyDependency : IMyDependency { private readonly ILogger<MyDependency> _logger; public MyDependency(ILogger<MyDependency> logger) { _logger = logger; } public Task WriteMessage(string message) { _logger.LogInformation( "MyDependency.WriteMessage called. Message: {MESSAGE}", message); return Task.FromResult(0); } }
第三步:把IMyDependency 服務註冊到服務容器中。
public void ConfigureServices(IServiceCollection services) { //註冊將服務生命期的範圍限定為單個請求的生命期,下節再來聊服務生命期 services.AddScoped<IMyDependency, MyDependency>(); }
第四步:把服務註入到使用它的類的構造函數中。在HomeController裡面調用IndexModel.OnGet方法輸出WriteMessage消息。
public class IndexModel : PageModel { private readonly IMyDependency _dependency; public IndexModel(IMyDependency dependency) { _dependency = dependency; } public void OnGet() { _dependency.WriteMessage("IndexModel.OnGet created this message."); } } private readonly IMyDependency _iMyDependency; public HomeController(IMyDependency iMyDependency) { _iMyDependency = iMyDependency; } public IActionResult Index() { IndexModel _IndexModel = new IndexModel(_iMyDependency); _IndexModel.OnGet(); return View(); }
WriteMessage日誌消息如下:
5.預設服務容器替換
下麵我們將來演示內置容器怎麼替換為其他容器示例,比如替換第三方 Autofac容器,我們需要在Startup.ConfigureServices方法裡面註冊Autofac容器,具體代碼如下:
public IServiceProvider ConfigureServices(IServiceCollection services) { // Add Autofac var containerBuilder = new ContainerBuilder(); containerBuilder.RegisterModule<DefaultModule>(); containerBuilder.Populate(services); var container = containerBuilder.Build(); return new AutofacServiceProvider(container); }
這裡需要註意的是如果需要使用第三方容器,Startup.ConfigureServices 必須返回 IServiceProvider。然後自定義一個模塊類配置依賴關係,具體代碼如下:
public class DefaultModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType<CharacterRepository>().As<ICharacterRepository>(); } }
應用程式在運行時,使用 Autofac 來解析類型,並註入依賴關係。
參考文獻:
在ASP.NET Core依賴註入