學習依賴注入的起點
因為被指出不熟 ASP.NET Core,我開始補 .NET Core 的相關知識,前後買了幾本書。
第一本:

說實在的,第一本不太適合我。兩岸用語不同,專有名詞讀起來有些障礙;有些段落也不夠簡潔,我常要反覆讀才明白。看完另一本依賴注入的書後,這種感受更明顯。不過它對 .NET Core 的基本原理、跨平台實作和 Middleware 解釋得很完整,仍有參考價值,只是我需要花較多時間消化。
第二本:

讀完第一本 ASP.NET Core 3 後,我知道依賴注入是 ASP.NET Core 的核心概念,但當時仍沒看懂相關說明,所以又買了第二本。
第二本讀起來清楚許多,我很推薦。
我怎麼理解依賴注入
書中用插座比喻依賴注入。我也試著用「武林世界」來理解 IoC(控制反轉)、組合根、建構子注入、屬性注入、方法注入和 DI 容器。這些名詞單獨硬記很抽象,放進同一個比喻後反而容易掌握。
武林高手要內外兼修:內功心法決定根基,武學招式則是實際做法。
在這個比喻裡,IoC 控制反轉像內功心法。先看原書的名詞定義:完全精通微軟Web架構: ASP.Net Core 3最新強實戰
控制反轉(Inverse of Control)
對於任何一項工作,不論大小,基本上都可以分解成對應的步驟,所以任何一項工作的實施都有固定的流程,而IoC的控制就是視作『針對流程的控制』。一般來講,設計模式是為了解決一個實際問題的方案,但是IoC沒有一個針對性的問題領域,本身也沒有提供一種可操作的解決方案,所以認為視為IoC是一種『設計原則』
由以上的定義可以瞭解,控制反轉是一個概念,一個設計原則,當我們在定義介面的時候,對於功能的切割是由哪些類別來注入,實際的控制權是由外部注入的,所以『針對流程的控制』要如何定義的靈活與彈性就要靠日後的練習,完全就是一個心法的概念。
什麼是『組合根』
組合根用來組合應用程式中的類別。
建立實體物件的工作集中在同一處,這個位置就是「組合根」。它通常位於應用程式的啟動點;在 .NET Core 中就是 Main 方法。
建構子注入
在 public 建構子中,以參數宣告所需的依賴,並由外部提供物件。
如果要增加依賴,應在同一個建構子加入參數,避免用多載造成混淆。
屬性注入
屬性注入就是透過公開屬性提供依賴,所以也稱為設值注入。
通常我們會在 「外部使用者要能夠隨時切換依賴對象」或是「類別已經有預設值了,但希望提供使用者可以覆寫掉預設值的彈性」時用到屬性注入
方法注入
當依賴對象會隨每次方法呼叫而不同,或依賴本身不變但使用方會變化時,可以透過方法參數設定依賴關係。
上述這些定義就是依賴注入的方法,組合根就是在定義如何引用各個類別,組合起所有的類別,這些方式就是類似拳腳基本功,有了基本的拳腳功夫,接下來就是討論到DI容器,DI容器可以說是各個門派的獨有的技能。
DI容器技術
DI 容器是一套軟體函式庫,用來自動處理物件關聯組合與物件生命週期。
依賴注入還有不少細節,這裡不展開,可參考
依賴注入:原理、實作與設計模式 (Dependency Injection: Principles, Practices, Patterns, 2/e)
第七章到第九章的內容
DI 容器主要協助管理與自動化,並提供各種註冊方法。以下記錄這些方法;繼續沿用前面的武林比喻,就當作加入華山派劍宗。
Autofac DI 容器筆記
Autofac .NET Inversion of Control Container
public void ConfigureContainer(ContainerBuilder builder)
{
Assembly domainAssembly = typeof(ITimeProvider).Assembly;
builder.RegisterType<DefaultTimeProvider>().As<ITimeProvider>().SingleInstance();
builder.RegisterInstance<IUserContext>(new AspNetUserContextAdapter());
builder.Register(c => new CommerceContext(this.Configuration.ConnectionString))
.InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(domainAssembly).AsClosedTypesOf(typeof(ICommandService<>));
builder.RegisterGenericDecorator(
typeof(AuditingCommandServiceDecorator<>), typeof(ICommandService<>));
builder.RegisterGenericDecorator(
typeof(SaveChangesCommandServiceDecorator<>), typeof(ICommandService<>));
builder.RegisterGenericDecorator(
typeof(SecureCommandServiceDecorator<>), typeof(ICommandService<>));
// ---- Start code section 13.4.4 ----
builder.RegisterAssemblyTypes(domainAssembly)
.As(type =>
from interfaceType in type.GetInterfaces()
where interfaceType.IsClosedTypeOf(typeof(IEventHandler<>))
select new KeyedService("handler", interfaceType));
builder.RegisterGeneric(typeof(CompositeEventHandler<>))
.As(typeof(IEventHandler<>))
.WithParameter(
(p, c) => true,
(p, c) => c.ResolveNamed("handler", p.ParameterType));
// ---- End code section 13.4.4 ----
// Register adapters to external systems
builder.RegisterAssemblyTypes(typeof(WcfBillingSystem).Assembly)
.AsImplementedInterfaces();
// Register repositories
builder.RegisterAssemblyTypes(typeof(SqlProductRepository).Assembly)
.Where(type => type.Name.EndsWith("Repository"))
.AsImplementedInterfaces();
}
TODO:持續學習中。
RegisterType
RegisterInstance
RegisterAssemblyTypes
RegisterGenericDecorator
參考資料:





