17

閱讀了越來越多關於IoC容器之後,我讀了this post關於沒有在您的代碼中使用IoC.Resolve()等。刪除對IoC容器的依賴

我真的很好奇知道那麼,我該如何去除對容器的依賴?

我想寫類似下面的代碼:

public void Action() 
{ 
    using(IDataContext dc = IoC.Resolve<IDataContext>()) 
    { 
     IUserRepository repo = IoC.Resolve<IUserRepository>(); 
     // Do stuff with repo... 
    } 
} 

但我怎麼能擺脫IoC.Resolve的電話?也許我需要更好地瞭解DI ...

在此先感謝。

回答

17

一般而言,大多數依賴項可以在創建時注入到您的類中。但是,在這種特殊情況下,您需要一個必須在使用時按需創建的組件。在這種情況下,完全刪除對IoC容器的依賴是非常困難的。我的方法一直是創建一個在創建時注入到類中的工廠,這反過來又封裝了所有直接的IoC用法。這樣就可以模擬來測試你的工廠,而不是IoC容器本身......這往往是一個容易得多:

// In Presentation.csproj 
class PresentationController 
{ 
    public PresentationController(IDataContextFactory dataContextFactory, IRepositoryFactory repositoryFactory) 
    { 
     #region .NET 4 Contract 
     Contract.Requires(dataContextFactory != null); 
     Contract.Requires(repositoryFactory != null); 
     #endregion 

     _dataContextFactory = dataContextFactory; 
     _repositoryFactory = repositoryFactory; 
    } 

    private readonly IDataContextFactory _dataContextFactory; 
    private readonly IRepositoryFactory _repositoryFactory; 

    public void Action() 
    { 
     using (IDataContext dc = _dataContextFactory.CreateInstance()) 
     { 
      var repo = _repositoryFactory.CreateUserRepository(); 
      // do stuff with repo... 
     } 
    } 
} 

// In Factories.API.csproj 
interface IDataContextFactory 
{ 
    IDataContext CreateInstance(); 
} 

interface IRepositoryFactory 
{ 
    IUserRepository CreateUserRepository(); 
    IAddressRepository CreateAddressRepository(); 
    // etc. 
} 

// In Factories.Impl.csproj 
class DataContextFactory: IDataContextFactory 
{ 
    public IDataContext CreateInstance() 
    { 
     var context = IoC.Resolve<IDataContext>(); 
     // Do any common setup or initialization that may be required on 'context' 
     return context; 
    } 
} 

class RepositoryFactory: IRepositoryFactory 
{ 
    public IUserRepository CreateUserRepository() 
    { 
     var repo = IoC.Resolve<IUserRepository>(); 
     // Do any common setup or initialization that may be required on 'repo' 
     return repo; 
    } 

    public IAddressRepository CreateAddressRepository() 
    { 
     var repo = IoC.Resolve<IAddressRepository>(); 
     // Do any common setup or initialization that may be required on 'repo' 
     return repo; 
    } 

    // etc. 
} 

這種方法的好處是,雖然你不能完全消除IOC依賴本身,您可以將其封裝在單一類型的對象(工廠)中,將大部分代碼與IoC容器解耦。例如,從一個IoC容器切換到另一個(即Windsor到Ninject),這可以提高您的代碼敏捷性。

應該指出,這樣做的一個有趣的結果是,你的工廠通常是通過它們使用的相同的IoC框架注入到他們的家屬。例如,如果您正在使用Castle Windsor,則可以創建配置,告訴IoC容器在創建時將兩個工廠注入到業務組件中。業務組件本身也可能有一個工廠......或者它可以簡單地由相同的IoC框架注入到一個更高級別的組件中,等等。

+0

感謝您的回答。唯一的問題是我使用的IoC應該有一個相對於'using'語句的範圍。然後,如果我解析說'IDataContext'它將解決該特定範圍的*單個實例*。我不希望我的控制器等意識到IoC容器,但是真的有辦法解決這個問題嗎? – TheCloudlessSky 2010-07-01 10:54:44

+0

我想知道如果一個控制器應該能夠調用IoC.Resolve ?如果不是,誰應該打這個電話? – TheCloudlessSky 2010-07-01 12:01:07

+0

你能解釋一下這個範圍嗎?您使用的是什麼IoC容器?一般來說,以任何方式將任何代碼耦合到容器框架是一種消極的耦合方式......您應該不惜一切代價避免這種情況。根據我的經驗,範圍(或上下文)很少(如果有的話)需要IoC容器工作。如果它以這種方式工作,我會找到一個替代容器,或者找到一種方法將這種上下文提供給解決您的對象的工廠,並儘可能保持您的IoC框架解耦。 – jrista 2010-07-01 16:24:38

1

有第二個依賴注入器注入第一個,並有第一個注入第二個注入。

+0

你可能會笑,但我已經看到人們認真地提倡這一點。 – mschaef 2010-06-30 23:14:36

2

前段時間我曾經參與過一個項目,該項目還沒有在IoC容器上解決。他們通過禁止他們的容器的非IoC特定功能來管理這種不確定性,並且通過將他們自己的類包裝爲Resolve。這也是我在博客文章中提到過的幾次...刪除最後的依賴關係,依賴注入容器的依賴關係。

這是一種可行的方法,但在某些時候,您必須選擇使用的工具,並願意接受您將支付切換到替代工具的費用。對我而言,IoC容器屬於您可能應該全心全意接受的事物類別,因此我質疑這種保理水平。如果你想進一步調查此,我建議以下鏈接:

http://blog.objectmentor.com/articles/2010/01/17/dependency-injection-inversion

2

一種替代方法是重新編寫接受Func<T>代表的方法。這將刪除方法的依賴,並允許您單元測試與模擬:

public void Action(Func<IDataContext> getDataContext, Func<IUserRepository> getUserRepository) 
{ 
    using(IDataContext dc = getDataContext()) 
    { 
     IUserRepository repo = getUserRepository(); 
     // Do stuff with repo... 
    } 
} 
2

我的博客上講述就此問題最近:

+0

感謝您的鏈接。所以,它再次歸結爲使用工廠。從我讀到的內容來看,爲了實例化工廠,您仍然需要參考IoC容器,對嗎? – TheCloudlessSky 2010-07-01 12:19:55

+0

不,你不需要任何參考容器來獲得工廠,這就是整個問題。 – 2010-07-01 12:57:58

+0

好,所以工廠有參考容器呢?工廠在哪裏生活?我瞭解工廠是如何創建的,我只是不知道該把它放在哪裏。 – TheCloudlessSky 2010-07-01 16:03:58