IoC 容器,在編譯時檢查錯誤
我有一個簡單的問題。
假設我有一個 .Net 解決方案,有不同的項目,如一些類庫(bll、dal 等)和一個可以是 web 應用程序或 wpf 應用程序的主項目,沒關係。
現在假設我想使用 IoC 容器(如 Windsor、Ninject、Unity 等)來解決驗證器、儲存庫、通用介面實現等內容。
我把它們放在一起。編譯並執行良好。然後,有一天,我添加了一個新服務,並在我的程式碼中嘗試通過 IoC 容器解決它。問題是,我忘記在 IoC 配置中註冊它。
一切都編譯,應用程序被部署並執行。一切正常,除了頁面程式碼向容器請求該新服務時,容器回答“嘿,我對這項服務一無所知”。
您會記錄您的錯誤,以及使用者友好的錯誤頁面。您將去檢查錯誤,查看問題並修復它。很標準。
現在假設我們想要改進這個過程,並且以某種方式能夠在編譯時知道我們期望 IoC 容器處理的每個服務是否在程式碼中正確註冊。
這怎麼可能實現?一件事,單元測試被排除在可能的答案之外,我正在尋找另一種方法,如果它確實存在的話。
想法?
編輯 - 經過一些回答和評論,似乎單元測試確實是實現此功能的唯一方法。
我想知道的是,如果單元測試 - 出於任何原因 - 不可能,因此無法在編譯時測試 IoC,這是否會阻止您使用 IoC 容器並選擇在您的程式碼中直接實例化? 我的意思是,您是否認為使用 IoC 和後期綁定太不安全和太冒險,並且看到它的優勢被這個“缺陷”所超越?
編譯器不可能驗證整個程序的工作。您的程序編譯的事實並不意味著它可以正常工作(即使不使用 IoC)。為此,您將需要自動化測試和手動測試。這並不意味著您不應該嘗試讓編譯器盡可能多地做,但出於這個原因遠離 IoC 是不好的,因為 IoC 旨在保持您的應用程序靈活、可測試和可維護。如果沒有 IoC,您將無法正確測試您的程式碼,並且如果沒有任何自動化測試,幾乎不可能編寫任何規模合理的可維護軟體。
然而,擁有 IoC 提供的靈活性確實意味著某些特定程式碼段所具有的依賴關係無法再由編譯器驗證。所以你需要以另一種方式做到這一點。
一些 DI 框架允許您驗證容器的正確性。例如,Simple Injector
Verify()包含一個方法,該方法將簡單地遍歷所有註冊並為每個註冊解析一個實例。通過在應用程序啟動期間呼叫此方法(或使用類似方法),您將在(開發人員)測試期間發現 DI 配置是否有問題,它會阻止應用程序啟動。你甚至可以在單元測試中做到這一點。然而重要的是,測試 DI 配置不需要太多維護。如果您必須為註冊的每種類型添加一個單元測試來驗證容器,那麼您將失敗,僅僅是因為缺少註冊(因此缺少單元測試)將是首先失敗的原因。
這為您提供了“幾乎”編譯時支持。但是,您需要注意應用程序的設計以及將事物連接在一起的方式。以下是一些提示:
- 遠離隱式屬性注入,如果找不到已註冊的依賴項,則允許容器跳過注入屬性。這將不允許您的應用程序快速失敗,並將導致
NullReferenceExceptions 稍後。顯式屬性注入,強制容器注入屬性很好,但是,盡可能使用建構子注入。- 如果可能,顯式註冊所有根對象。例如,
Controller在容器中顯式註冊所有 ASP.NET MVC 實例。這樣容器可以檢查從根對像開始的完整依賴圖。您應該以自動方式註冊所有根對象,例如通過使用反射來查找所有根類型。例如,Simple Injector的MVC3 集成 NuGet 包包含一個RegisterMvcControllers擴展方法,可以為您執行此操作。其他容器的集成包也包含類似的特性。- 如果註冊根對像不可行或不可行,請在啟動期間手動測試每個根對象的創建。例如,對於 ASP.NET Web 窗體
Page類,您可能會從其建構子中呼叫容器(因為Page不幸的是,類必須具有預設建構子)。這裡的關鍵是使用反射一次找到它們。通過使用反射查找所有 Page 類並對其進行實例化,您將發現(在應用程序啟動或測試期間)您的 DI 配置是否存在問題。- 讓您的 IoC 容器為您管理的所有服務都有一個公共建構子。多個建構子會導致歧義,並可能以不可預知的方式破壞您的應用程序。擁有多個建構子是一種反模式。
- 在某些情況下,在應用程序啟動期間還無法創建某些依賴項。為了確保應用程序可以正常啟動並且仍然可以驗證其餘的 DI 配置,請將這些依賴關係抽像到代理或抽象工廠後面。
我已經使用 roslyn SourceGenerators 編寫了一個編譯時 IOC 容器,如果您犯了錯誤,它確實會提供編譯時警告和錯誤。
當然,有時只能在執行時提供某些東西,但有一些方法可以明確地做到這一點,這意味著如果缺少依賴項,我們仍然可以給你錯誤。