如何使用 IOC 容器构造对象

How to construct objects with an IOC container

我相信我对依赖注入有足够的了解,可以开始使用它,但我在理解 IOC 容器与服务位置以及如何使用容器构建我的对象时遇到了困难。

给定:

public interface IFooService
{
    void DoSomethingFooey();
}
public class FooService : IFooService
{
    readonly IBarService _barService;

    public FooService(IBarService barService)
    {
        this._barService = barService;
    }

    public void DoSomethingFooey()
    {
        // some stuff
        _barService.DoSomethingBarey();
    }
}


public interface IBarService
{
    void DoSomethingBarey();
}
public class BarService : IBarService
{
    readonly IBazService _bazService;

    public BarService(IBazService bazService)
    {
        this._bazService = bazService;
    }

    public void DoSomethingBarey()
    {
        // Some more stuff before doing ->
        _bazService.DoSomethingBazey();
    }
}


public interface IBazService
{
    void DoSomethingBazey();
}
public class BazService : IBazService
{
    public void DoSomethingBazey()
    {
        Console.WriteLine("Blah blah");
    }
}

如果没有 IOC 容器,我将不得不像这样在构造时提供我的所有依赖项:

    public void DoStuff()
    {
        // Without a container, I would have to do something yucky like:
        FooService fs = new FooService(
            new BarService(
                new BazService()
            )
        );

        // or
        BazService bazService = new BazService();
        BarService barService = new BarService(bazService);
        FooService fooService = new FooService(barService);
    }

通过这种 DI 方式构建我的 类 我似乎收获了很多,因为我现在可以独立测试我的 类,而在使用 DI 之前我真的不能。

但是根据我正在阅读的有关进行 DI 的 "proper way" 的内容,我会使用像 Unity 或 StructureMap 这样的 IOC 容器...但我还没有找到我需要了解的确切内容开始吧。

我假设我的容器看起来像这样:

var container = new Container(_ =>
{
    _.For<IFooService>().Use<FooService>();
    _.For<IBarService>().Use<BarService>();
    _.For<IBazService>().Use<BazService>();
});

取自以下示例:http://structuremap.github.io/quickstart/

根据上面的示例,我不确定包含 var container... 的方法的签名是什么样的,或者它是如何被调用并保持在范围内的。也许更重要的是如何使用容器实际构建对象。

如果我想创建一个 FooService 的实例(然后 BarServiceBazSerivce),我的 DoStuff() 现在会是什么样子?

之前是:

public void DoStuff()
{
    // Without a container, I would have to do something yucky like:
    FooService fs = new FooService(
        new BarService(
            new BazService()
        )
    );

    // or...
}

但是现在使用我的容器会是什么样子?我的方法如何知道在哪里寻找我的容器?

希望我走在正确的轨道上,但如果我不是,请告诉我,以及我遗漏了什么。

我通常这样做是为了保留 DI 并避免 IOC 容器的缺点:

public interface IFooService
{
    void DoSomethingFooey();
}
public class FooService : IFooService
{
    readonly IBarService _barService;

    public FooService() : this(new BarService()) {}

    public FooService(IBarService barService)
    {
        this._barService = barService;
    }

    public void DoSomethingFooey()
    {
        // some stuff
        _barService.DoSomethingBarey();
    }
}


public interface IBarService
{
    void DoSomethingBarey();
}
public class BarService : IBarService
{
    readonly IBazService _bazService;

    public BarService() : this(new BazService()) {}

    public BarService(IBazService bazService)
    {
        this._bazService = bazService;
    }

    public void DoSomethingBarey()
    {
        // Some more stuff before doing ->
        _bazService.DoSomethingBazey();
    }
}


public interface IBazService
{
    void DoSomethingBazey();
}
public class BazService : IBazService
{
    public void DoSomethingBazey()
    {
        Console.WriteLine("Blah blah");
    }
}

修改为使用容器的 DoStuff 方法如下所示:

public void DoStuff()
{
    IFooService fooService = container.GetInstance<IFooService>();

    // do something with fooService
}

我基于您引用的 StructureMap link。其他 IoC 容器以不同方式命名其等效 "GetInstance" 方法。

您通常不会在尝试获取 IFooService 实例的同一方法中创建 container。通常,您会在某些初始化方法中的其他地方配置您的容器,并将容器保存在您可以轻松访问的地方,例如在静态 属性 中。因此,您的 DoStuff 方法可能看起来更像这样:

public void DoStuff()
{
    IFooService fooService = MyIoCContainer.Current.Getinstance<IFooService>();

    // do something with fooService
}

其中MyIoCContainer只是你定义的class,它的Current属性是你在初始化时配置的StructureMap容器​​

但是,您并不总是需要编写这样的代码才能获得服务。一些框架提供了钩子来使用你的 IoC 容器,这样你就可以在你的 classes 中继续使用依赖注入,并且框架负责使用你的 IoC 容器实例化你的 classes。

仅举一个例子,ASP.NET MVC 框架提供了一个 IDependencyResolver 接口和 DependencyResolver class,您可以使用它来允许该框架在以下情况下使用您的 IoC 容器在您的 Web 应用程序中实例化您的控制器 classes。这样,您就可以继续使用依赖注入一直到您的控制器中,因此根本不需要在您的控制器中显式引用您的 IoC 容器。请参阅 this section on "Including a Custom Dependency Resolver" 了解这是如何完成的。

因此,理想情况下,您的 DoStuff 方法及其包含的 class 看起来像这样,这只是依赖注入的进一步延续。那么问题是这个 MyStuffDoer class 是如何被实例化的:要么通过一个了解如何使用你的 IoC 容器来创建它的框架,要么通过你在某处编写一些显式代码以从 IoC 容器实例化它.

class MyStuffDoer
{
    IFooService fooService;

    public MyStuffDoer(IFooService fooService)
    {
        this.fooService = fooService;
    }

    public void DoStuff()
    {
        // do something with fooService
    }
}