如果說在之前的 dotNET 版本中,依賴注入還是個比較新鮮的東西,那么在 dotNET Core 中已經是隨處可見了,可以說整個 dotNET Core 的框架是構建在依賴注入框架之上,本文講解下對 dotNET Core 中依賴注入的理解,
什么是依賴
在面向物件的語言中,所說的依賴通常指類與類之間的關系,比如有個用戶類 User 和日志類 Log , 在 User 類中需要記錄日志,就需要引入日志類 Log,這樣 User 類就對 Log 類產生了依賴,代碼如下:
public class User
{
private Log _log=new Log();
public string GetUserName()
{
_log.Write("獲取用戶名稱");
return "oec2003";
}
}
public class Log
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
或者直接在類的方法中對其他類進行了依賴,如下:
public class Dept
{
public string GetDeptNameByUserId(string userId)
{
return "開發部";
}
}
public string GetUserFullName(string userId)
{
Dept dept=new Dept();
return $"oec2003({dept.GetDeptNameByUserId(userId)})";
}
這樣的類與類之間的直接依賴有如下幾個問題:
- 要換一種 Log 的實作方式,所有參考的地方都要進行修改;
- 如果整個專案中到處都是這種類與類之間的強關聯,代碼維護會變得非常困難;
- 對單元測驗不友好,
要解決上面的問題,需要將依賴的類抽象成介面,不直接依賴具體的實作類類,而是依賴介面,這就是面向物件的六大原則中的依賴倒置原則:高層模塊不應該依賴于底層模塊,二者都應該依賴于抽象;抽象不應該依賴于實作細節,實作細節應該依賴于抽象,
User 類調整后的代碼如下:
public interface ILog
{
void Write(string message);
}
public class Log:ILog
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
public class User
{
private ILog _log;
public User(ILog log)
{
_log = log;
}
public string GetUserName()
{
_log.Write("獲取用戶名稱");
return "oec2003";
}
}
- 創建了 ILog 介面;
- User 類調整為對 ILog 的依賴;
- 在 User 中類添加建構式,在建構式中傳入介面 ILog 的實體,
那么建構式中的實體什么時候創建呢?這時就需要用到注入了,
什么是注入?
在上面示例中,注入就是在某個時機,將 ILog 的實體傳遞到 User 類的建構式中,而 User 類中根本就不關心 ILog 的實作,在 dotNET Core 中提供了一個內置的服務容器 IServiceProvider,然后在 Startup 類的 ConfigureServices 方法中進行注冊,注冊代碼如下:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IUser, User>();
services.AddSingleton<ILog, Log>();
services.AddControllers();
}
- 上面代碼中是以單例的模式進行注冊;
- 將原來在 User 類中的對 Log 類的直接依賴,轉移到了 ConfigureServices 方法中,這便是控制反轉(IoC)了,Log 類采用什么實作,不在 User 類中來控制,而是轉移到了框架層面,
借助框架的依賴注入,相比較我們自己在類中互相關聯依賴地去創建物件有以下好處:
- 方便管理類之間的依賴,對我們使用面向物件的設計原則有幫助;
- 代碼有更好的維護性和擴展性;
- 可以方便管理各個物件的生命周期,
依賴涉及的核心類
在 dotNET Core 中使用內置的依賴注入需要引入 using Microsoft.Extensions.DependencyInjection;i 命名空間,相關的幾個核心型別如下:
- IServiceCollection:利用此類的擴展方法進行服務的注冊;
- IServiceProvider:由 IServiceCollection 創建的依賴注入容器體現為,IServiceProvider 介面;
- ServiceDescriptor:具體注冊的服務的描述,IServiceProvider 就是通過這個描述來構建我們需要的服務實體;
- ServiceLifetime:一個服務生命周期的列舉,有 Singleton、Scoped、Transient 三種型別,
服務注冊的生命周期
服務注冊的生命周期有三種:
- Singleton:單例模式,創建在全域的 IServiceProvider 的根容器上;
- Scoped:范圍模式,用 Scope 注冊的物件,在同一個 ServiceProvider 的 Scope 下相當于單例;
- Transient:瞬時模式,每次從容器獲取物件時都是得到的一個全新的物件,
下面用一個示例來看下這三種不同生命周期的區別
1、創建分別代表不同生命周期的介面和類,代碼如下:
public interface ISingletonService{}
public interface IScopedService{}
public interface ITransientService{}
public class SingletonService:ISingletonService{}
public class ScopedService:IScopedService{}
public class TransientService:ITransientService{}
2、在 Controller 中創建介面方法:
[HttpGet]
public void GetService([FromServices]ISingletonService singleton1,
[FromServices]ISingletonService singleton2,
[FromServices]IScopedService scoped1,
[FromServices]IScopedService scoped2,
[FromServices]ITransientService transient1,
[FromServices]ITransientService transient2
)
{
System.Console.WriteLine($"singleton1:{singleton1.GetHashCode()}");
System.Console.WriteLine($"singleton2:{singleton2.GetHashCode()}");
System.Console.WriteLine($"scoped1:{scoped1.GetHashCode()}");
System.Console.WriteLine($"scoped2:{scoped2.GetHashCode()}");
System.Console.WriteLine($"transient1:{transient1.GetHashCode()}");
System.Console.WriteLine($"transient2:{transient2.GetHashCode()}");
}
3、連續呼叫兩次該介面,輸入如下圖:

測驗示例中每個不同生命周期的物件都通過 FromServices 的方式注入了兩次,分析結果如下:
- Singleton:兩次請求的四個物件都相同;
- Scoped:相同請求的兩個物件是一致,重新請求會生成新的物件;
- Transient:兩次請求的四個物件都不相同,每次都構建新的物件,
總結
- 依賴注入的目的是為了解耦;
- 不依賴于具體類,而是依賴抽象類或者介面,這叫依賴倒置;
- 把服務的注冊和實體化的作業交給 dotNET Core 框架,而不是在具體實作類中處理,這個叫控制反轉即IoC (Inversion of Control)
就先寫到這兒了,dotNET Core 框架本身的依賴注入功能已經比較強大,但還是有些功能不能滿足,需要引入第三方的注入框架,關于如何引入第三方依賴注入框架以及為什么要用第三方依賴注入框架,后面單獨開篇寫,
示例代碼:https://github.com/oec2003/DotNetCoreThreeAPIDemo/tree/master/InjectDemo
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/20536.html
標籤:.NET Core
