Dependency Injection 初探筆記 (DI & IOC)
2021-06-16
初探依賴注入 Denpendency Injection Pattern(DI) 與控制反轉 Inversioin of Control (IOC) 設計模式。
說明
什麼是依賴注入 💉
依賴注入是一種設計模式,目的是要減少程式碼的耦合程度,讓擴充與維護變得更容易。同時也因為 ASP.NET Core 將 DI 的觀念加入其框架之中,因此 DI 成為學習 ASP.NET Core 的基礎與背景知識。
而要使用依賴注入設計模式,重點在於結合使用 Interface,藉由將注入的物件介面化,相似於策略模式的使用方式,提升提供開發的擴充性,常見的注入方式包含:建構式注入、屬性注入、方法注入等。
依賴注入能夠帶來的好處 💗
將依賴的類別介面化,並如同策略模式一般替換演算法
使用裝飾模式,將依賴的類別可以層層包裝的方式加入功能,卻不影響使用的類別
藉由替換依賴類別的方式,可以產生專為測試情境需要的演算法,增加程式的可測試性
DI In ASP.NET Core
We make mvc support first, then we can use dependency injection in controller.
builder.Services.AddControllersWithViews();
var app = builder.Build();
app.MapDefaultControllerRoute();
app.Run();
We add a services folder and then add a service class.
public interface IMyservice
{
Guid Id { get; }
}
public class MyService : IMyservice
{
public Guid Id { get; } = Guid.NewGuid();
// ⚠️ This will generate a new Guid every time
// public Guid Id => Guid.NewGuid();
}
We using a controller to inject the service.
public class HomeController(IMyservice _service) : Controller
{
public IActionResult Index()
{
ViewData["Id"] = _service.Id;
return View();
}
}
And also inject the service into the view.
@inject Mod05.Services.IMyservice Service
<div>Controller: @ViewData["Id"]</div>
<div>View: @Service.Id</div>
By the way, if we don't register the service, we will get an error like this picture.
We do register service in program.cs
, and we can use AddTransient
, AddScoped
, AddSingleton
to register service.
builder.Services.AddTransient<IMyservice, MyService>();
builder.Services.AddScoped<IMyservice, MyService>();
builder.Services.AddSingleton<IMyservice, MyService>();
Service Type | Description |
---|---|
Transient | A new instance is created every time it is requested (Id always different) |
Scoped | A new instance is created once per request (Id same in same requests) |
Singleton | A single instance is created for the lifetime of the application (Id always same) |
If you are curious about when then service is disposed, you can use IDisposable
interface.
public class MyService : IMyservice, IDisposable
{
public Guid Id { get; } = Guid.NewGuid();
public void Dispose()
{
Console.WriteLine($"{Id} disposed");
}
}
At Transient
and Scoped
, the service will be disposed when the request is finished. And remebmer that Transient
are created every time, so it will be disposed every time. And at Singleton
, the service will be disposed when the application is stopped.
More
建構式注入, Constructor Injection
在寫 ASP.NET MVC 5 的時候,就會思考在 Service Layer 或者 Business Layer 也仿照 HomeController 去起始一個 DBContext 會不會造成資源的負擔,此外該 DBContext 在使用完畢後是否會自動釋放?
後來雖然研究的結果是 EntityFramework 具備處理的機制,不需要特別去覆寫 Dispose 也不會造成資料庫連線數的負擔,同時實際觀察 Database 的 Session 確實沒有多個 Session 來自 ASP.NET MVC 因此後來也沒有特別費心在此。
只是當時有思考過可以藉由 Controller 在 DBContext 傳送給 Business Layer 來使用,隱約也是有建構式注入的精神。
public interface IReadable
{
void ReadData();
}
public class XMLReader : IReadable
{
public void ReadData()
{
throw new NotImplementedException();
}
}
public class CSVReader : IReadable
{
public void ReadData()
{
throw new NotImplementedException();
}
}
其中 Reader Class 是受其他類別所依賴的。
public class Reader
{
private IReadable _reader;
public Reader(IReadable reader)
{
_reader = reader;
}
public void Read()
{
_reader.ReadData();
}
}
由執行的類別負責決定其所依賴的物件。
static void Main()
{
var reader = new Reader(new XMLReader());
reader.Read();
}
DI Container
使用 DI Container 可以帶來下列好處:
- 自動註冊模型
- 自動解析物件的生成依賴
- 物件生命週期的管理
常見的 DI Container 包括 AutoFac、Ninject、Unity 等, ASP.NET Core 則是有原生的 DI Container 可以使用。
參考資料
ASP.Net Core MVC 進化之路 - Dependency Injection概念介紹
ASP.NET MVC 專案分層架構 Part.6 - DI/IoC 使用 Unity.MVC
相關連結
Pluralsight - Getting Started with Dependency Injection in .NET