05 | 依賴注入:良好架構的起點
為什么要使用依賴注入框架
- 借助依賴注入框架,我們可以輕松管理類之間的依賴,幫助我們在構建應用時遵循設計原則,確保代碼的可維護性和可擴展性
- ASP.NET Core 的整個架構中,依賴注入框架提供了對象創建和生命周期管理的核心能力,各個組件相互協作,也是由依賴注入框架的能力來實現的
組件包
- Microsoft.Extensions.DependencyInjection.Abstractions
- Microsoft.Extensions.DependencyInjection
依賴注入的核心是以上兩個組件包,一個是抽象包,一個是具體的實現
這里用到了一個經典的設計模式,接口實現分離模式
組件只需要依賴抽象接口,而不需要依賴具體實現,當使用的時候注入它的具體實現即可
這樣做的好處是可以在使用時決定具體的實現,也就意味着未來可以做任意的擴展,替換依賴注入框架的具體實現
默認情況下,使用 .NET Core 提供的內置依賴注入框架,也可以使用第三方的依賴注入框架來替換默認實現
核心類型
- IServiceCollection:服務的注冊
- ServiceDescriptor:每一個服務注冊時的信息
- IServiceProvider:具體的容器,由 ServiceCollection Build 產生
- IServiceScope:一個容器的子容器的生命周期
生命周期
- 單例 Singleton:在整個根容器的生命周期內,都是單例,不管是子容器還是根容器,與作用域的區別是:一個是全局的,一個是范圍的單例
- 作用域 Scoped:在 Scope 的生存周期內,也就是容器的生存周期內,或者子容器的生存周期內,如果我的容器釋放掉,我的對象也會釋放掉
- 瞬時(暫時)Transient:每一次從容器里面獲取對象時,都可以得到一個全新的對象
新建一個 ASP.NET Core Web 應用程序 DependencyInjectionDemo,選擇API
添加一個 Services 文件夾,新建三個服務代表三個生命周期的服務
namespace DependencyInjectionDemo.Services
{
public interface IMyScopedService { }
public class MyScopedService : IMyScopedService
{
}
}
namespace DependencyInjectionDemo.Services
{
public interface IMySingletonService { }
public class MySingletonService : IMySingletonService
{
}
}
namespace DependencyInjectionDemo.Services
{
public interface IMyTransientService { }
public class MyTransientService : IMyTransientService
{
}
}
在 Startup 中注冊服務
public void ConfigureServices(IServiceCollection services)
{
#region 注冊服務不同生命周期的服務
// 將單例的服務注冊為單例的模式
services.AddSingleton<IMySingletonService, MySingletonService>();
// Scoped 的服務注冊為 Scoped 的生命周期
services.AddScoped<IMyScopedService, MyScopedService>();
// 瞬時的服務注冊為瞬時的生命周期
services.AddTransient<IMyTransientService, MyTransientService>();
#endregion
services.AddControllers();
}
在 Controller 里面獲取我們的服務
// FromServices 標注的作用是從容器里面獲取我們的對象
// 每個對象獲取兩遍,用於對比每個生命周期獲取的對象是什么樣子的
// HashCode 代表對象的唯一性
[HttpGet]
public int GetService(
[FromServices]IMySingletonService singleton1,
[FromServices]IMySingletonService singleton2,
[FromServices]IMyTransientService transient1,
[FromServices]IMyTransientService transient2,
[FromServices]IMyScopedService scoped1,
[FromServices]IMyScopedService scoped2)
{
Console.WriteLine($"singleton1:{singleton1.GetHashCode()}");
Console.WriteLine($"singleton2:{singleton2.GetHashCode()}");
Console.WriteLine($"transient1:{transient1.GetHashCode()}");
Console.WriteLine($"transient2:{transient2.GetHashCode()}");
Console.WriteLine($"scoped1:{scoped1.GetHashCode()}");
Console.WriteLine($"scoped2:{scoped2.GetHashCode()}");
Console.WriteLine($"========請求結束========");
return 1;
}
注釋 Get 方法
//[HttpGet]
//public IEnumerable<WeatherForecast> Get()
//{
// var rng = new Random();
// return Enumerable.Range(1, 5).Select(index => new WeatherForecast
// {
// Date = DateTime.Now.AddDays(index),
// TemperatureC = rng.Next(-20, 55),
// Summary = Summaries[rng.Next(Summaries.Length)]
// })
// .ToArray();
//}
啟動程序,刷新瀏覽器再次訪問接口,輸出如下:

單例模式兩次的 HashCode 沒有變化
兩個瞬時服務兩次的 HashCode 完全不同,意味着瞬時服務每次請求都會得到一個新對象
范圍服務每個請求內是相同的,不同的請求之間得到的對象實例是不同的
GitHub源碼鏈接:


本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。
歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含鏈接: http://www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改后的作品務必以相同的許可發布。
如有任何疑問,請與我聯系 (MingsonZheng@outlook.com) 。

