☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
concept-backend 中級

DI 依賴注入:為什麼不直接 new?

先看問題:直接 new 有什麼不好?

public class OrderService {
    private readonly SqlOrderRepository _repo = new SqlOrderRepository(); // ← 直接 new
    private readonly SmtpEmailService _email = new SmtpEmailService();     // ← 直接 new
}

表面上可以動,但隱藏了三個嚴重問題:

問題 1:無法替換(緊耦合)

OrderService 直接依賴 SqlOrderRepository
→ 換成 MongoOrderRepository?要改 OrderService 的程式碼
→ 違反「開閉原則」(對擴展開放,對修改封閉)

問題 2:無法測試

// 想測試 OrderService 的邏輯,但它直接 new 了真的資料庫連線
// 你不能注入一個假的 Repository → 無法做單元測試
[Test]
public void CreateOrder_ShouldSendEmail() {
    var service = new OrderService(); // ← 會真的連資料庫!測試環境沒有 DB 就爆了
}

問題 3:無法管理生命週期

new SqlOrderRepository() → 每次都建新連線
→ 連線池爆掉
→ 你無法控制「整個 Request 共用一個」或「全域只有一個」

DI 怎麼解決?

核心概念:把依賴「注入」進來,不要自己 new

// ❌ 直接 new(控制權在 OrderService 內部)
public class OrderService {
    private readonly SqlOrderRepository _repo = new SqlOrderRepository();
}

// ✅ 依賴注入(控制權在外部)
public class OrderService {
    private readonly IOrderRepository _repo;

    public OrderService(IOrderRepository repo) { // ← 從外部注入
        _repo = repo;
    }
}

控制反轉(IoC):「我不自己建立依賴,我要求別人給我。」


三種生命週期

builder.Services.AddSingleton<ICacheService, RedisCacheService>();  // 全域一個
builder.Services.AddScoped<IOrderRepository, SqlOrderRepository>(); // 每個 Request 一個
builder.Services.AddTransient<IEmailService, SmtpEmailService>();   // 每次注入都 new 一個
生命週期 何時建立 何時銷毀 適合場景
Singleton 應用啟動時 應用關閉時 快取、設定、HttpClient
Scoped 每個 HTTP Request Request 結束時 DbContext、Repository
Transient 每次注入時 超出作用域時 輕量無狀態服務

常見陷阱:Captive Dependency

Singleton 注入 Scoped → ❌ Scoped 物件被 Singleton 抓住,永遠不會釋放
→ DbContext 被快取,資料永遠是舊的
→ ASP.NET Core 預設會拋出例外提醒你

不用 DI 的替代方案

方式 做法 缺點
直接 new new SqlRepo() 緊耦合、不可測試
Service Locator ServiceLocator.Get<IRepo>() 隱藏依賴、難以追蹤
Factory Pattern RepoFactory.Create() 比 DI 囉唆,但有時合理
靜態方法 OrderRepo.GetAll() 無法 mock、全域狀態

DI 不是唯一選擇,但在 Web 應用中是最適合的,因為 HTTP Request 的生命週期天然適合 Scoped。


什麼時候「不需要」DI?

  • 工具類(Math.Max()string.Format())→ 靜態就好
  • 簡單的值物件(DTO、Record)→ 直接 new
  • 一次性的 Console App → 過度設計
  • 沒有替換需求、不需要測試的小程式

原則:如果一個類別有「行為」且你可能想替換或測試它,就用 DI。


面試角度

Q: 為什麼要用 DI?
A: 解耦合、可測試、生命週期管理。讓類別不需要知道依賴的具體實作。

Q: Scoped 和 Singleton 差在哪?
A: Scoped 是每個 Request 一個實例(適合 DbContext),Singleton 全域共用(適合快取)。

Q: 什麼是 IoC?
A: 控制反轉。傳統是類別自己 new 依賴,IoC 是由外部(容器)提供依賴。DI 是 IoC 的一種實現方式。

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊