2014-07-17 23 views
2

我在寫一個類庫(用C#),它將與我無法控制的應用程序一起分發。我的庫也有點安全敏感,因此我不希望允許調用應用程序對類庫的依賴項進行任何配置。它必須是獨立的,並初始化它自己的依賴關係。什麼是在類庫中初始化IoC容器的好策略?

同時,我希望它是單位可測試和鬆耦合和我想使用一個IoC容器來管理的依賴關係。目前我正在使用內部構造函數和[InternalsVisibleTo()],以便單元測試可以執行手動注入。我更喜歡使用Ninject作爲我的IoC容器,但這與我想的問題無關。

由於類庫實際上沒有定義的入口點,所以我很努力想出一個在生產環境中初始化我的IoC容器的好策略,它有許多可以實例化的類沒有辦法知道應用程序首先使用哪一個。

我想知道是否有可能是某種AssemblyLoad事件,乃至AppDomain似乎有這樣的活動,但我的裝配必須已經被加載到AppDomain中之前,我甚至可以掛接到這一點,所以我總是錯過了我自己的裝配加載的事件。我也考慮過使用靜態初始化器或構造函數,但我並不是很樂意使用IoC容器設置來污染每個可能的類,因爲這會使它們緊密耦合到容器。我不想解耦我的代碼,然後將它耦合到IoC容器。

我發現討論這個話題的幾個其他問題,但他們都不跟我的情況真的交易。一個建議使用靜態初始化器,另一個建議應用程序應始終是組合根。

還有別的辦法嗎?

+0

您的客戶是否會使用Ninject來實現您的實現?如果不是,你打算如何使用Ninject向你的課程注入任何東西? –

+0

我的消費者可能會或可能不會使用Ninject,我不知道。我的類庫可能會作爲NuGet包使用,所以我會在該級別上依賴於Ninject。 –

回答

1

有兩種情況在這裏:

A)你的消費者在不使用Ninject容器:

如果你不希望他們能夠提供可選配置(​​或者被注入內部類),你必須創建一個構造函數來自己解決這些依賴關係。這將是你的入口點。

B)你的消費者使用Ninject容器:

您需要將您的Ninject內核實例暴露在你的消費者。如果你想隱藏你正在使用Ninject的事實,或者只是暴露內核本身,它可以被包裝在ServiceLocator中。無論哪種情況,您都可以將其作爲靜態類的屬性,這將成爲您的入口點。

從內部的角度來看,我更喜歡使用選項B,但作爲第三方庫的頻繁消費者,我從來沒有見過任何人暴露自己的IoC容器,不是我非要他們。我只是想能夠實例化一個類,而不用擔心任何內部實現或依賴關係。當然,你的小米可能會有所不同。

+0

+1感謝Troels。我認爲(A)最適合我的情況。我會仔細考慮一下。 –

4

有你的要求之間存在着矛盾。

首先,你不想因爲安全問題的組成根。其次,您希望依賴由一個容器來解決。但是由於庫不能控制容器,幾乎任何東西都可以注入到代碼中,包括那些試圖從內部破壞任何東西的東西。

一種方法是將您的依賴關係顯式化,以便庫客戶端負責提供您的依賴關係。

namespace Library 
{ 
    public class Foo1 
    { 
     // a classical IoC dependendency 
     public Foo1(IBar bar) 
     { 
     } 
    } 
} 

。另一方面,用合成根是初始化你的庫仍然並不意味着你的庫被污染的明確外點。 CR與本地工廠(也稱爲依賴關係解析器)很好地協作,它負責創建內部對象,並從CR內部進行設置。

namespace Library 
{ 
    public interface IFoo { } 

    // local Foo factory, with a customizable provider 
    public class FooFactory 
    { 
     private static Func<IFoo> _provider; 
     public static void SetProvider(Func<IFoo> provider) 
     { 
      _provider = provider; 
     } 

     public IFoo CreateFoo() 
     { 
      return _provider(); 
     } 
    } 

    // Bar needs Foo 
    public class Bar 
    { 
     public void Something() 
     { 
      // you can use the factory here safely 
      // but the actual provider is configured elsewhere 
      FooFactory factory = new FooFactory(); 

      IFoo foo = factory.CreateFoo(); 
     } 
    } 
} 

,然後在Composition根的地方(靠近應用程序的入口點)

// kernel is set up to map IFoo to an implementation of your choice 
public void ComposeRoot(IKernel kernel) 
{ 
    FooFactory.SetProvider(() => kernel.Get<IFoo>()); 
} 

正如你所看到的,而不是可能在多個類別提供多個注射點,當地工廠是單注入點,它提供整個庫的完整配置,使其成爲獨立的。

你甚至可以擁有一個不涉及任何IoC容器的提供者,而是創建一個具體的實現,從而使得它容易在沒有任何容器的情況下進行測試。切換到另一個IoC非常簡單,您只需提供另一個提供商。

您的圖書館規模越大,您的課程就具有凝聚力(經常使用彼此),更方便的是擁有本地工廠。你並不需要重新拋出你的類之間的依賴關係(沒有本地工廠,如果你的類A需求I和你B需求A然後自動B需求I),相反,所有的類都依賴於一個單一的工廠。

+0

+1謝謝Wiktor,很多人想到那裏。我不能依靠應用程序充當組合根,所以我認爲也許本地工廠可以工作,也許從靜態初始化程序配置。 –

+0

問題是您不希望您的庫直接依賴於Ninject。初始化器必須在外部。組成的根源是它的共同點,是的,我相信你可以依靠它。當一個工廠方法被調用時,你只是拋出異常,但是初始化器並沒有被調用。例外說「在調用工廠方法之前必須調用初始化程序」。這清楚地表明出了什麼問題,所以任何人都可以輕鬆修復它。 –

+0

我越想越多,我越傾向於這種方法。我想我會忘記使用Ninject,並且只需要一個具有某種靜態初始化器的工廠類。至少這樣我只能聯繫到工廠類,沒有任何外部。我會試驗一下並回報。 –

相關問題