删除IoC容器的依赖关系

时间:2010-06-30 23:10:30

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

在阅读了越来越多关于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 ......

提前致谢。

5 个答案:

答案 0 :(得分: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容器切换到另一个IoC容器(即Windsor到Ninject)。

应该注意的是,这个有趣的结果是,您的工厂通常通过他们使用的相同IoC框架注入其家属。例如,如果您使用Castle Windsor,则会创建配置,告知IoC容器在创建时将两个工厂注入业务组件。业务组件本身也可能有工厂......或者,它可能只是由相同的IoC框架注入更高级别的组件等,ad inf。

答案 1 :(得分:2)

我刚刚参与了一个没有安置在IoC容器上的项目。他们通过禁止容器的非IoC特定功能以及用自己的类包装Resolve来管理不确定性。这也是我在博客文章中多次提倡的内容...删除最后一个依赖项,依赖于依赖注入容器。

这是一种可行的技术,但在某些时候你必须选择你使用的工具,并愿意接受你需要支付成本来转换到替代工具。对我来说,IoC容器属于你可能应该全心全意接受的事物类别,所以我质疑这个级别的因子分解。如果你想进一步研究这个问题,我建议如下链接:

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

答案 2 :(得分:2)

另一种方法是重新编写方法以接受Func<T>个委托。这将从方法中删除依赖项,并允许您使用mock进行单元测试:

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

答案 3 :(得分:2)

我最近在博客中谈到了这个问题:

答案 4 :(得分:1)

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