【问题标题】:Remove Dependency on IoC Container移除对 IoC 容器的依赖
【发布时间】:2010-06-30 23:10:30
【问题描述】:

在阅读了越来越多有关 IoC 容器的信息后,我阅读了 this post 关于在您的代码中没有 IoC.Resolve() 等。

我真的很想知道,我怎样才能去除对容器的依赖?

我想编写如下代码:

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

但是我怎样才能摆脱 IoC.Resolve 调用呢?也许我需要更好地了解 DI...

提前致谢。

【问题讨论】:

    标签: c# dependency-injection inversion-of-control ioc-container


    【解决方案1】:

    一般来说,大多数依赖项都可以在创建类时注入到您的类中。但是,在这种特殊情况下,您需要一个必须在使用时按需创建的组件。在这种情况下,很难完全消除对 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 框架注入到更高级别的组件中,等等,ad inf。

    【讨论】:

    • 感谢您的回答。唯一的问题是我使用的 IoC 应该有一个范围,相对于 using 语句。然后,如果我 Resolve 说IDataContext,它将为该特定范围解析 single instance。我不希望我的控制器等知道 IoC 容器,但真的有办法解决这个问题吗?
    • 我想知道的是控制器是否应该能够调用 IoC.Resolve?如果不是,谁应该打这个电话?
    • 你能解释一下这个范围吗?您使用的是什么 IoC 容器?一般来说,以任何方式将您的任何代码耦合到容器框架都是一种消极的耦合……您应该不惜一切代价避免这种情况。根据我的经验,IoC 容器的工作很少需要范围(或上下文),如果有的话。如果它确实以这种方式工作,我会找到一个替代容器,或者找到一种方法将该上下文提供给解析您的对象并尽可能保持您的 IoC 框架解耦的工厂。
    • 其实我正在尝试编写自己的非常简单 IoC 容器。我这样做主要是为了我自己的学习。我正在使用 ASP.NET MVC,因此无论是谁调用 Resolve 的 IoC 容器。我了解使用工厂,但工厂在哪里?它应该是应用程序中唯一引用容器的东西吗?
    • 是的,工厂应该是唯一引用您的 IoC 容器的东西。至于你把工厂放在哪里,我想这取决于你。这最终取决于这些工厂的主要消费者是谁。有些人喜欢在他们的 API 中包含工厂,但是我认为这会在 API、工厂和工厂创建的实现之间产生不希望的耦合。我的偏好通常是将工厂定位在消费者旁边……在你的情况下,这将是你的控制器。将它们放入自己的项目中,一个带有接口,另一个带有实现
    【解决方案2】:

    另一种方法是重写方法以接受Func&lt;T&gt; 委托。这从方法中删除了依赖关系,并允许您使用模拟对其进行单元测试:

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

    【讨论】:

      【解决方案3】:

      不久前,我参与了一个尚未确定 IoC 容器的项目。他们通过禁止容器的非 IoC 特定功能以及用自己的类包装 Resolve 来管理不确定性。这也是我在博文中看到过几次提倡的……删除最后一个依赖,对依赖注入容器的依赖。

      这是一种可行的技术,但在某些时候,您必须选择您使用的工具,并愿意接受您为切换到替代工具而付出的代价。对我来说,IoC 容器属于你可能应该全心全意拥抱的东西,所以我质疑这种级别的分解。如果您想进一步研究,我建议以下链接:

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

      【讨论】:

        【解决方案4】:

        我最近写了一篇关于这个问题的博客:

        【讨论】:

        • 感谢您的链接。因此,它再次归结为使用工厂。从我读到的内容来看,为了实例化工厂,您仍然必须引用 IoC 容器,对吗?
        • 不,你不需要任何对容器的引用来获取工厂,这就是重点。
        • 好的,那么工厂有对容器的引用吗?工厂住在哪里?我了解工厂是如何创建的,只是不知道该放在哪里。
        • 我注意到您在博客文章中谈到了一个对 Resolve 的调用。我正在尝试实现自己的 IoC 容器(用于学习)并且想知道 如何 这可以实现?
        • 您还声明您为“root”(在我的情况下为 MVC 控制器)进行了一次 Resolve 调用,它会去哪里?
        【解决方案5】:

        有第二个依赖注入器来注入第一个,并让第一个注入第二个。

        【讨论】:

        • 你可能会笑,但我看到有人提倡这一点,显然是认真的。
        猜你喜欢
        • 2016-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多