1

我使用IoC(DI)方法並且通常具有參數,這些參數是由最低層(DB層等)從配置設置(即連接字符串,靜態值等)讀取的。什麼是最好的方式來做到這一點?配置設置和IoC

  1. 直接在此閱讀的最底層,即:

    string sendGridApiKey = ConfigurationManager.AppSettings["SendGridApiKey"]; 
    

它的工作原理,但還需要添加此關鍵單元測試項目的配置文件。此外,程序集取決於配置文件

  1. 在最高層(即web應用程序)中讀取它並從所有層中拋出參數?它會起作用,但所有的中間層都會得到參數,這些參數不會被使用(所以它們將取決於不使用的東西)。

當最低層的不同實現需要不同的參數時也存在問題。即SendMail1可以要求SMTP /登錄/密碼,但SendMail2可以只需要ApiKey,但SendMail1和SendMail2應該實現相同的接口。所以,它會造成使用方法#2的困難

回答

1

選項1從一個更簡單的解決方案開始,但很快就會變得很難測試,需要參考,打破圍繞從最高層到最低層流動值的模式等。

推薦的模式是#2,其中最高層將所有依賴關係及其值分配給較低層。

儘管您必須將其傳遞到所有圖層,但您的DI引擎應該在自動鏈接分辨率方面爲您提供幫助。

例如

如果你的控制器需要實例化一個業務層類,它需要實例化存儲庫類,它需要一個Connection類,需要設定值時,你不需要做手工,在3個地方。

您可以在DI引擎中分別定義BL類,Repository類和Connection類的註冊,它將負責爲您實例化控制器。

它可能看起來乏味,但通常從長遠來看具有很大的好處。 (根據明確的合同定義,單元測試,沒有反模式,孤立的擔憂等)

如果您真的擔心通過它3個地方,工廠和總體服務方面有各種選擇。每個人都有自己的優點/缺點,並取決於您使用的DI引擎。讓我們知道選項2是否絕對不可接受。

例如Autofac允許您將很多構造函數參數包裝到單個聚合服務接口中,以便Autofac可以爲您注入。

+0

謝謝您的答覆。我在問題中增加了一段。請閱讀 –

+1

@OlegSh請添加SendMail1和2的定義,以及界面。通常很容易向具體類添加特定的參數..構造函數可以注入特定的參數 –

+1

這對DI/IoC策略來說通常是一個很好的答案,因此您不會緊密耦合各種組件。但是這些組件本身應該負責從設置獲取配置(或者您處理配置設置)。他們可能有重載參數,但假設SendMail1和SendMail2具有相同數量的設置。也許sendmail一個是活動目錄,需要一個域,並且sendmail 2需要一些其他的auth方法(username // password // token) – Prescott

3

上述兩種方法都無法正常工作 - 首先(讀取服務中的配置)阻止您提及的單元測試;第二步(從頂層傳遞配置)需要了解頂層所有可能的每種服務實現。

我喜歡依賴於兩者配置存儲器和類型爲每個接口註冊的對象的的DI容器的知識的方法:在登記時間

  • 通配置 - 即,如果容器支持登記工廠方法,工廠方法可以讀取配置和比調用特定的構造函數具體的服務的

    // constructor: publc ConcreteServiceX(int setting1, string setting2)... 
    container.RegisterFactory<IServiceX>(
        container => return new ConcreteServiceX(42, ReadSetting("X")); 
    
  • 寄存器配置用於每個服務的在容器中的類/接口

    // constructor: publc ConcreteServiceX(IConcreteServiceXSettings settings)... 
    container.RegisterType<IService,ConcreteServiceX>(); 
    container.RegisterInstance<IConcreteServiceXSettings>(
        new ConcreteServiceXSettings(42, ReadSetting("X")); 
    

兩種方法本地化配置系統的知識,以一個地方(容器配置),並允許每個服務(上型配置存儲器的沒有依賴性)以及更高級別的對象的更容易的單元測試(不需要了解服務的任何設置)。


注:樣品使用統一的語法,採取到你選擇的容器