前言
ASP.NET Core 應用在啟動過程中會依賴各種元件提供服務,而這些元件會以介面的形式標準化,這些元件這就是我們所說的服務,ASP.NET Core框架建立在一個底層的依賴注入框架之上,它使用容器提供所需的服務。要了解依賴注入容器以及它的機制,我們需要了解什麼是依賴注入。
控制反轉
說道依賴注入就不得不提控制反轉(IoC)。
定義: 高層模組不應該依賴低層模組,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。
相對於細節的多變性,抽象的東西要穩定的多。以抽象為基礎搭建起來的架構比以細節為基礎搭建起來的架構要穩定的多。在.NET中,抽象指的是介面或者抽象類,細節就是具體的實現類,使用介面或者抽象類的目的是制定好規範和契約,而不去涉及任何具體的操作,把展現細節的任務交給他們的實現類去完成。
好萊塢法則
“不要給我們打電話,我們會給你打電話(don‘t call us, we‘ll call you)”這是著名的好萊塢原則。在好萊塢,把簡歷遞交給演藝公司後就只有回家等待。由演藝公司對整個娛樂項的完全控制,演員只能被動式的接受公司的差使,在需要的環節中,完成自己的演出。IIOC的原理就是基於好萊塢原則,所有的元件都是被動的(Passive),所有的元件初始化和呼叫都由容器負責。
以ASP.NET MVC開發來說,我們只需按照約定的規則(定義好的目錄或命名規則)定義對應的controller和View檔案即可。整個框架會根據路由規則解析的引數到目標Controller,如果目標Action方法需要呈現一個View,框架會根據約定找到對應的的View檔案(.cshtml檔案),對其進行動態編譯生成html回覆給客戶端,整個框架都體現了IoC思想。
流程控制
IoC是將流程的控制從應用程式當中遷移到框架當中,框架利用一個引擎驅動整個流程的自動執行。應用程式無須關心工作流程的細節,它只需要啟動這個引擎即可。框架會以替丁的形式提供擴充套件點,應用程式通過註冊擴充套件的方式實現對某個環節的控制。一旦這個引擎(容器)被啟動,註冊的擴充套件就會自動參與整個流程的執行。
通過上面這張圖不難看出IoC在其中起到的作用。
未使用前: 整個程式相互依賴,當新的需求被提出時,牽一髮而動全身,這是我們最不想看到的,在小專案中還能理清關係,當需求越來越多,簡直不可想象。
開始使用: 在引入第三方後,各個模組之間沒有耦合關係,將依賴降至最低,所有控制都通過IoC集中控制。
使用後: 為了方便觀察把中間的IoC容器拿掉後,可以看出各個模組之間已經沒有耦合關係,修改單一模組後,再也不需要考慮其他模組。
三種依賴注入方式
1.構造器注入
構造器注入就是在建構函式中藉助引數將依賴的物件注入由他建立的物件當中。平時基本都是使用其中的建構函式方式實現注入。
public class A
{
public IB B { get; }
public A(IB b) => B = b;
}
ASP.NET Core 中的使用
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[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();
}
}
2.屬性注入
通過標註InjectionAttribute特性的方式可以將屬性設定為自動注入的依賴屬性。
public class A
{
public IB B { get; set; }
[Injection]
public IC C { get; set; }
}
3.方法注入
同樣通過標註InjectionAttribute特性的方式可以將該方法標註為注入方法。
public class A
{
public IB B { get; }
[Injection]
public Initialize(IB b) => B = b;
}
除了通過容器初始化服務過程中自動呼叫實現,我們還可以利用它實現另一種更加自由的方法注入,這種方式在ASP.NET Core中廣範應用。在ASP.NET Core啟動時會呼叫Startup物件完成中介軟體註冊,而定義Startup型別時候不需要讓他實現某個介面,所以註冊Configure方法沒有一個固定宣告,但可以通過下面方法將任意依賴服務註冊到這個方法當中。
public class Startup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication1 v1"));
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
生命週期
AddSingleton的生命週期: 專案啟動-專案關閉 相當於靜態類 只會有一個
AddScoped的生命週期: 請求開始-請求結束 在這次請求中獲取的物件都是同一個
AddTransient的生命週期: 請求獲取-(GC回收-主動釋放) 每一次獲取的物件都不是同一個
注意:由於AddScoped物件是在請求的時候建立的,所以不能在AddSingleton物件中使用,甚至也不能在AddTransient物件中使用。
權重: AddSingleton→AddTransient→AddScoped
ASP.Net Core 中自帶的注入
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IA, A>;
services.AddSingleton<IB, B>;
services.AddTransient<IC, C>;
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" });
});
}
注意: ASP.Net Core中的注入還是比較簡單的,但是當服務變得越來越多時,手動注入就比較麻煩了,後續在介紹其他IoC框架。