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