作者的評價#
本章的基調與前兩章截然不同。作者對 Microsoft.Extensions.DependencyInjection(以下簡稱 MS.DI)給出了直接且批判性的評價:
「它的功能如此有限,不適合任何有一定規模、實踐鬆耦合的應用程式。」
這個強烈的立場貫穿全章,作者逐項說明 MS.DI 的設計意圖與實際限制。
基本架構#
MS.DI 同樣採用兩階段設計:
// 階段一:配置 ServiceCollection
var services = new ServiceCollection();
services.AddTransient<IProductService, ProductService>();
// 階段二:建構 ServiceProvider
var provider = services.BuildServiceProvider(
validateScopes: true); // 建議開啟 Scope 驗證
Figure 15.1: Microsoft.Extensions.DependencyInjection 的使用模式:先配置,再解析
註冊方式#
MS.DI 以方法名稱直接表達 Lifetime,三種註冊方法對應三種生命週期:
services.AddTransient<IService, ServiceImpl>(); // 每次新建
services.AddSingleton<IService, ServiceImpl>(); // 單例
services.AddScoped<IService, ServiceImpl>(); // 每 Scope 一個當同一個介面有多個註冊時,MS.DI 採用 Last Registration Wins 語意——後註冊的會靜默覆蓋先前的,不會產生任何錯誤或警告。這在大型專案中極容易導致難以追蹤的 bug。
Scope 管理#
與 Autofac 類似,MS.DI 要求從 IServiceScope 解析物件:
using (var scope = provider.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<IProductService>();
}永遠從 IServiceScope 解析,而非直接從 Root ServiceProvider。 直接從 Root 解析會導致 Scoped 物件無法被正確釋放。

Figure 15.3: MS.DI 的 Scope 作為可共享元件的容器
主要限制#
本章的重點在於詳述 MS.DI 的多項重大限制:
無 Decorator 支援#
MS.DI 沒有內建的 Decorator 註冊機制。要實現 Decorator Pattern,必須使用 Lambda 手動包裝:
services.AddTransient<IRepository, SqlRepository>();
// 痛苦的 Decorator 手動包裝
services.AddTransient<IRepository>(provider =>
{
var inner = new SqlRepository(
provider.GetRequiredService<IDbContext>());
return new CachingRepository(inner);
});這種做法不僅冗長,而且失去了 Auto-Wiring 的好處——必須手動解析 Decorator 內層的所有依賴。
無 Composite 支援#
同樣缺乏內建的 Composite Pattern 支援,需要類似的 Lambda workaround。
無 Auto-Registration / Assembly 掃描#
MS.DI 不提供任何 Assembly 掃描或慣例式註冊機制。每一個型別映射都必須手動逐一註冊。這意味著——
- 失去了 DI Container 相對於 Pure DI 最大的優勢
- 大型應用程式的 Composition Root 會變得冗長且重複
無驗證與診斷#
MS.DI 沒有 Verify() 之類的驗證方法:
- 無法在啟動時檢測註冊是否完整
- 無法偵測 Captive Dependencies(如 Singleton 持有 Scoped 物件)
- 這類錯誤只能在執行期被使用者觸發時才會浮現
無條件式註冊#
不支援根據 context 或 predicate 條件性地註冊不同的實作。
設計初衷#
理解這些限制後,需要認識 MS.DI 的設計初衷:
- 它是為框架和函式庫開發者設計的最小公約數容器
- 目標是提供一個所有 .NET 框架(ASP.NET Core、EF Core 等)能共同依賴的最低限度 DI 抽象
- 並非為應用程式開發者提供完整功能的 DI 解決方案
如果你的應用程式只需要基本的 Constructor Injection 和簡單的 Lifetime 管理,MS.DI 可能就夠用了。但一旦你需要 Decorator、Composite、Auto-Registration 或驗證機制,就應該考慮使用 Autofac 或 Simple Injector。
與其他容器的對比#
| 功能 | Autofac | Simple Injector | MS.DI |
|---|---|---|---|
| Decorator 支援 | 內建 | 內建(含條件式) | 無 |
| Composite 支援 | 內建 | 內建 | 無 |
| Auto-Registration | 支援 | 支援 | 無 |
| 驗證 / 診斷 | 有限 | Verify() 完整支援 | 無 |
| 泛型型別約束 | 無 | 有(編譯期安全) | 無 |
| Captive Dependency 偵測 | 無 | 有 | 無 |
MS.DI 重點回顧
- 作者明確認為 MS.DI 不適合有一定規模的鬆耦合應用程式
- Last Registration Wins 語意可能導致靜默覆蓋的隱性 bug
- 缺乏 Decorator、Composite、Auto-Registration、驗證等關鍵功能
- 設計定位是框架開發者的最小公約數容器,非完整 DI 解決方案
- 如果需要進階功能,應改用 Autofac 或 Simple Injector