一.概念
剛接觸 .NET CORE MVC ,不習慣但卻非常重要的當屬於依賴注入。與 .Net MVC 對比,.NET CORE MVC 在架構上更傾向靠依賴注入處理對象的傳遞(引用),以前習慣於使用靜態對象、或實例化類等的場景,在.NET CORE MVC 中一般改成從構造函數顯式請求依賴項。
可以說依賴注入 是 .NET CORE MVC 核心,目的在於減少不同對象之間的耦合度,也能大幅提高系統的可測試度,應用程式更易於測試和維護。
為更好解耦,.NET CORE MVC 使用接口,而非直接調用具體對象實體。舉個例子:
使用緩存,過去直接用 MemoryCache.Default就可以了,但在.NET CORE MVC Controller 中要用MemoryCache , 要先在 StartUp註冊服務,然後在 Controller的構造函數中獲取實例,進而在Controller的方法中使用。
另:我們經常會接觸到控制反轉(IOC)概念,但它與依賴注入可以說是同一個概念的不同角度描述。
二.NET CORE MVC 依賴注入的生命週期
這里的生命週期指:透過依賴取得某個對象時,是每次要求得建立一顆新對象,還是從頭到尾共用一個 Instance (執行個體)。
有三種:
1.Singleton(單例)
整個處理只建立一個 Instance,任何時候都共用它。
2. Scoped
在網頁 一個Request 過程(指接到瀏覽器請求到回傳結果前的執行期間)共用一個 Instance。
3. Transient
每次要求元件時就建立一個新的,永不共用。
例如:
Startup.cs 的ConfigureServices 中进行注册
...
services.AddTransient<IDataService, DataService>();
services.AddSingleton<IDataService, DataService>();
services.AddScoped<IDataService, DataService>();
...
另注:被註冊元件間也會彼此依賴,有個原則是生命週期長的不能參考生命週期比他短的,例如:註冊為 Singleton 的元件不能依賴註冊成 Scoped 的元件, 原因很明顯,如果生命週期比自己短,可能所依賴元件在後期已被消減無法使用。
一般來說,要整個 Process 共用一份的服務可註冊成 Singleton,EF Context (提醒:不要跨進程共用) 建議註冊成 Scoped,以方便 DB 連線重複使用, 若想保險點,註冊成 Transient 就可以了。
三.在.NET CORE MVC 中使用依賴注入
举个简单例子:顯示不同的当前時間格式
public interface IDateTime
{
DateTime Now { get; }
}
Public class DateTimeFormat1:IDateTime
{
Public DateTime Now
{
Return DateTime.Now.Tostring(“yyyy-MM-dd”);
}
}
Public class DateTimeFormat2:IDateTime
{
Public DateTime Now
{
Return DateTime.Now.Tostring(“MM/dd/yyyy”);
}
}
1.傳統的調用方法
Public class MyController:Controller
{
Private IdateTime dateTime = New DateTimeFormat1();
Public IActionResult GetDateTime()
{
Rerturn Content(dateTime.Now);
}
}
以上傳統模式直接NEW 一個具體實例對象(這也是很多初學者或者很多老手最經常使用的方式),如果後期要改變引用的對象,例如日期格式從DateTimeFormat1"—"改為DateTimeFormat2 “///”,那就要在全系統中所有使用該NEW對象的地方更改,代碼耦合度太高,非常不利於維護。
2.使用依賴注入方式
1)在Startup.cs的ConfigureServices方法中设置注入
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDateTime, DateTimeFormat1>();
....
}
2)在Controller 中
public class MyController:Controller
{
private readonly IdateTime _dateTime;
public MyController (IDateTime dateTime) //在構造函數中獲取實例
{
_dateTime= dateTime;
}
[HttpGet]
Public IActionResult GetDateTime()
{
Rerturn Content(_dateTime.Now);
}
}
使用依賴注入方時,解決了傳統方式耦合度高的問題,如果後期變更實現,只要在startup.cs中将
services.AddTransient<IdateTime, DateTimeFormat1>();
變更成
services.AddTransient<IdateTime, DateTimeFormat2>()
即可;
无须在所有使用IdateTime的地方做任何改動;也可以非常簡單的設置生命週期(Transient,Scoped,Singleton);
以上MVC Controller 依赖注入的做法是透過构造函数參數方式,是典型做法。
除此之外,我們也可以有其他方式:
第1種:Action 加上 [FromServices] Attribute ,直接注入到controller的方法,不用構造器注入。
public IActionResult About([FromServices] IDateTime dateTime)
{
return Content( $"Current server time: {dateTime.Now}");
}
第2種:可以直接在View中獲取注入
例如:@inject IDateTime dateTime
<ul> <li>Now: @dateTime.Now</li> </ul>
第3種:在httpcontext裏直接獲取注入
HttpContext.RequestServices.GetService<IDateTime >();
注意:在 ASP.NET Core 沒有 HttpContext.Current 可用,如果要在 Controller/View 以外使用 HttpContext,要使用 IHttpContextAccessor。
例如:
public class DataService : IDataService
{
private readonly HttpContext _httpContext;
public DataService(IHttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
//...
}