4

normally, I'll use a dependency injection container (unity) in c# like this example:

class SomeClass
{
    private readonly ILogger _logger;

    public SomeClass()
    {
        _logger = DependencyContainer.Resolve<ILogger>();
    }

    public void DoSomething()
    {
        var someOtherClass = DependencContainer.Resolve();
        someOtherClass().whatElseEver();
    }
}

Yesterday, I've read some articles about correct dependency injection with a di container. After this, I know that my example is totally bad. This is not dependency injection this is just something like a service locator.

Okay, how to solve this? I think with constructor injection you can solve this problem easily:

class SomeClass
{
    private readonly ILogger _logger;
    private readonly ISomeOtherClass _someOtherClass;

    public SomeClass(ILogger logger, ISomeOtherClass someOtherClass)
    {
        _logger = logger;
        _someOtherClass = someOtherClass;
    }

    public void DoSomething()
    {
        _someOtherClass().whatElseEver();
    }
}

Now I have the correct implementation of the dependency injection principle. But how to wire up all these dependencies? I have a caller class which resolve all the required decencies of the class "SomeClass":

class SomeClassCaller
{
    public void DoSomething()
    {
        var logger = DependencyContainer.Resolve<ILogger>();
        var someOtherClass = DependencyContainer.Resolve();

        var someClass = new SomeClass(logger, someOtherClass);
        someClass.DoSomething();
    }
}

But this example still use a dependency container as a service locator, which is bad. In nearly every article about this, I've read, that the dependency container should only be used, at the application root/entry points, is this correct?

That means, no class should have the ability the resolve some dependencies with a "service locator" on the fly, like the logger. I've to inject the ILogger into nearly every class through the constructor, to avoid this problem, correct? This way feels really bad, if I take the ILogger through maybe 10 classes into every constructor, isn't it?

So, I've to use a dependencies container only, if I've a complex dependencie graph?

Can someone give me an example how it looks like, if you use a dependency container, only at the root of an application?

  • Have you looked at this? stackoverflow.com/questions/3825270/… – Esben Skov Pedersen Feb 26 '16 at 12:06
  • I think, that you haven't understand me correctly. In my opinion it's ugly, to take the ILogger dependencie trought all classes and inject it via constructor injection. Some dev's say, that you have to use your depdendency injection container only at the root of your application. So this is the reason, why you have to tale all depdencies all inject it into the constructor or something like that. In my opionion, this is ugly code, but how you can solve this problem better? – Marcel Hoffmann Feb 26 '16 at 12:12
  • Why do you think it's ugly? It's explicit what is required for making an object work. This is pretty neat IMHO. I may have understod the question as I thought it was more about resolving objects without the container. – Esben Skov Pedersen Feb 26 '16 at 12:46
  • @MarcelHoffmann Logging is a bit unusual as an example because it's what's often called a "cross-cutting concern". If you look that phrase up, you'll find ideas on how to deal with those. – Ben Aaronson Feb 26 '16 at 12:58
2

Consider these classes, and their dependencies explicitly listed as argumetns to the constructor.

class SomeClass {
    public SomeClass(IFoo foo, IBar bar) {}
}

class Foo : IFoo {
    public Foo(IBaz baz) {}
}

class Bar : IBar {}

class Baz :IBaz {}

The Graph would look like this:

  • SomeClass
    • IFoo
      • IBaz
    • IBar

Now, assembling this by Hand means creating instances the whole object graph, with its dependencies:

var baz = new Baz();
var foo = new Foo(baz);
var bar = new Bar();
var someClass = new SomeClass(foo, bar);

Using an IOC container with constructor injection is not really different. The thing you have to grasp is: You pull on one end, and the graph will follow:

container.add(IFoo, Foo);
container.add(IBar, Bar);
container.add(IBaz, Baz);
container.add(ISomeClass, SomeClass);

// now pull on your "Entry Point"/"Bootstrap":
container.get(ISomeClass);

The container will resolve and inject dependencies as needed. You do not have to resolve anything by hand, with one exception: This is your entry point.

  • Thank you this helps me a lot. In our company, we have done dependency incjection using a IoC container totally done, we're using an IoC container as an service locator. Now, it's clear for me, how to use an IoC container, thanks. – Marcel Hoffmann Feb 26 '16 at 19:57
  • I don't understand your last sentence. I understand it as you do have to resolve "This is your entry point", but everything else you don't have to resolve. What is the "This is your entry point" that needs resolving? – Dennis Jul 17 '17 at 18:14
1

The answer depends on one single factor which divides the problem into two branches:

You don't use an IoC framework

If you ever wanted to unit test your whole application, from the application root, then you would indeed use the DI container only in the application main function and pass everything down the line using dependency injection. But I have been developing for several years and I am yet to come to a situation when someone would actually want to do that. To make sure the application works as a whole, you have integration tests.

When deciding whether to use dependency injection for a class or instantiate its dependencies inside (be it by directly calling new or using a DI container), the rule of thumb is to ask whether you want to unit test the class you are creating.

If you want to unit test a class in the future, or perhaps you are even developing using the TDD approach, then DI is the way to go. If you do not care about unit testing the class you are creating, because it is merely a wrapper for 20 other smaller, testable classes, a wrapper acting as a procedure, instantiate its dependencies inside.

You use an IoC framework

If you're using an IoC framework, which is able to resolve dependencies (such as Guice for Java, Ninject for C# or Dice for PHP), use dependency injection for everything.

You will have instantiation in one place, which is great, and if you ever decided to unit test large chunks of your code, you are still able to do so.

  • If I decided to use an IoC framework, I have to wire up all depenencies at the root of an application, sure. But how I resolve all this dependencies in the application? I mean, you shouldn't use the IoC container in the business logic. Only at the entry points of an application like: main, global.asx. So how can I resolve dependencies in a complex class construct? It happes that some depper class needs a specific requirement, which I've wired up in the IoC container, but how can I get this dependencie? Form the container it self? - No. Should I inject this dependency trougth every class? – Marcel Hoffmann Feb 26 '16 at 15:00
  • So how can I resolve dependencies in a complex class construct? > The container does this for you, recursively, for everything it's trying to construct. Try it and see, it's not nearly as complicated as it sounds. – patchandthat Feb 26 '16 at 15:32
  • Okay. For example: I'va class a, b, c and d. My dependency graph looks like this: A -> B -> C -> D. So, you say, the container resolves everything, then class A need a constructor with three parameters: b,c and d. Class b needs a consturctor with two parameters, c and d. Yes, the container can resolve this, correct. But in my opinion, that I've take every dependency injected from class a to d is ugly. Maybe i dont understand something. If I'm not correct, please give me an example, how it works exactly. – Marcel Hoffmann Feb 26 '16 at 16:01
  • The only answer to a,b,c,d questions is: it depends. The is no single approach that covers all cases. In general one would only use the container at startup but there can indeed be cases where using the container as a service locator makes sense. – Cerad Feb 26 '16 at 16:26
  • @MarcelHoffmann I'm not exactly sure how Ninject works, because I am not a C# dev, but if it is anything like Dice for PHP, it works in a way you insert into the Ninject container your implementations, the container basically acts as a dictionary, where you say if a class requires this type of a class, deliver from the framework this implementation. Dice goes even further, that dependencies taking no parameters upon construction are resolved automatically, so you only need to resolve these, which take some kind of configuration in form of strings or other scalar types. – Andy Feb 26 '16 at 18:33

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.