2

Suppose I have 3 classes, Program, A (with dependencies D1 and D2) and B (with dependencies D3 and D4). Program initialises an IOC container and registers a bunch of types before creating an instance of A.

class Program
{
    static void Main(string[] args)
    {
        IOC ioc = new IOC();
        //register types

        A a = ioc.Resolve<A>();
    }
}

At some point later on, a needs to create an instance of B, injecting its two dependencies into the constructor.

Question: How should a resolve the dependencies of B? I have been led to believe that passing the IOC container around i.e. injecting it into A is the Service Locator pattern, which is bad. If B created C with its dependencies, B would also need to have the container injected, and the container would be littered throughout my code. This sounds like a testing nightmare. Using a global static doesn't sound much better.

Tom
  • 679
  • 1
  • 6
  • 15

3 Answers3

1

It's actually simple. If A needs B, it should accept B as constructor argument:

public class A
{
    private readonly D1 d1;
    private readonly D2 d2;
    private readonly B b;

    public A(D1 d1, D2 d2, B b) {
        this.d1 = d1;
        this.d2 = d2;
        this.b = b;
    }
}

public class B
{
    private readonly D3 d3;
    private readonly D4 d4;
    private readonly C c;

    public B(D3 d3, D4 d4, C c) {
        this.d3 = d3;
        this.d4 = d4;
        this.c = c;
    }
}

This way you build up object graphs recursively and you can get very deep object graphs, which is completely fine. This way you only need the container in the startup path of your application (a.k.a. the Composition Root).

Whether or not A needs B 'later on', or just sometimes, or hardly ever is irrelevant. Building object graphs should be fast and whether or not B is created needlessly should therefore not be an issue.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • If the construction of object `b` is dependant on some state in `a` that is set at run time, then what do you do? i.e. `b` cannot be created immediately and injected into `A`. – Tom Jul 09 '15 at 09:50
  • 1
    @Tom That's arguably a new question, and it's probably bad design that `A` and `B` are coupled that way –  Jul 09 '15 at 09:54
  • @Tom: the creation of your components should *NOT* depend on runtime state. Your components should be *stateless* and runtime data should be passed through the object graph using method calls. When you do this, the problem you have will go away completely. – Steven Jul 09 '15 at 10:37
  • @MickyDuncan Apologies, I'm new here. I will raise another question in future. – Tom Jul 09 '15 at 10:39
  • @Steven Thanks, I understand now. – Tom Jul 09 '15 at 10:41
  • @Tom Not a problem my friend. Wishing you well –  Jul 09 '15 at 10:58
  • @Tom You don't have to ask another question: http://stackoverflow.com/q/1943576/126014 – Mark Seemann Jul 09 '15 at 11:47
  • @MarkSeemann: I would rather say: "Any place where you need a run-time value to construct a particular dependency, you'll need to change your design". – Steven Jul 09 '15 at 12:02
  • @Steven Yes, that would be the appropriate first reaction, but that's only useful to people if they know how to do that :) – Mark Seemann Jul 09 '15 at 12:08
  • BTW, here are some examples of how to change the design in the face of run-time values: http://blog.ploeh.dk/2013/01/09/MetadataRoleHint http://blog.ploeh.dk/2013/01/10/RoleInterfaceRoleHint http://blog.ploeh.dk/2013/01/11/PartialTypeNameRoleHint The third option is my favourite. – Mark Seemann Jul 09 '15 at 12:10
  • 1
    @MarkSeemann Thanks Mark, they're useful links. I noticed you have a book. I'll be sure to check it out. – Tom Jul 09 '15 at 12:29
0

Ideally you should never inject container to your classes just to resolve the the dependencies. Rather you register instances in in separate place probably your startup class where in runtime application should be able to resolve dependencies automatically from the registered instances with container.

look at following example how an IOC container typically register its instances

Register

SimpleIoc.Default.Register< IDataService, DataService >(); SimpleIoc.Default.Register< MainViewModel >();
SimpleIoc.Default.Register< SecondViewModel >();

Resolve

SimpleIoc.Default.GetInstance< MainViewModel >();

kuhajeyan
  • 10,727
  • 10
  • 46
  • 71
  • Your solution uses a `static` which hampers testability e.g. when creating a instance of a class in a unit test, I have to set up the container first. How I need to set up the container is not immediately apparent from the code I'm testing. – Tom Jul 09 '15 at 09:39
  • 1
    @Tom _"Your solution uses a static which hampers testability..., I have to set up the container first"_ - Doesn't using a system require that the container be setup first? In testing or not? Using statics or not? –  Jul 09 '15 at 09:57
  • @MickyDuncan In an ideal world my unit tests wouldn't know anything about the IOC container. I would create mock objects and inject them manually. If my code under test holds a reference to a static container I would be forced to initialise the container for my tests. – Tom Jul 09 '15 at 10:38
0

At some point later on, A needs to create an instance of 'B', injecting its two dependencies into the constructor.

In this case, A must take a dependency on a BFactory that can create the B's for it.

David Osborne
  • 6,436
  • 1
  • 21
  • 35