Pregunta

What's so bad about manually wiring up dependencies via constructor injection and what's so great about using a DI container (container.Resolve<T>()) as against that?

I mean, apart from the fact that you have to type a few more keystrokes every time you need to create your dependent object and supply it with all its dependencies.

In fact, having a central registry like a DI Container provides can be a heck of a lot more confusing and take away control from you.

I've had this question and another one (below) for some time, but I guess Mark Seemann's article titled Service Locator is an anti-pattern got me to finally type this question here.

So, my other question is:

What if there's a dependency that itself needs some dynamic (say user supplied) input for its construction? Say, like:

class Dependency : IDependency
{
  public Dependency(string userInput) { }
}

class Dependent
{
    IDependency _dependency;

    void DoSomething()
    {
      var container = new MyFavoriteContainerFromTheMarket();

      Console.Write("Hey, user. How are you feeling?");
      var userInput = Console.ReadLine();

      // Do DI containers have overloads for Resolve instead of for Register
      // that accept parameters to be passed to constructors?
      // In other words, what if I don't know the constructor parameter
      // value at the time of registration of the dependency but only
      // get to know it just before I have to resolve/instantiate the
      // dependency? Do popular DI containers of today have overloads
      // for that? I assume they must
      _dependency = container.Resolve<IDependency>(userInput);
    }
}

Overall, doesn't it feel like DI Containers take away control from you? Wouldn't it just be okay to manually wire up dependencies in some cases?

Sure, I understand sometimes it is easy and saves you typing and creates for a cleaner, more terse code snippet, but having it all mixed up -- some dependencies managed by a container and some you have on your own that you manually supply -- all makes it so confusing and forces you to remember which one is which, and thus makes maintenance more difficult, wouldn't you agree?

Update

Oh, wait. I just realized my whole example was convoluted. I had meant to call the Resolve from client code in the main(). The code I should have originally posted with this question should have been like this:

class Dependency : IDependency
{
  public Dependency(string userInput) { }
}

class Dependent
{
    IDependency _dependency;

    public Dependent(IDependency dependency)
    {
      _dependency = dependency;
    }

    public void DoSomething()
    {
    }
}

class Program
{
  public static void Main(string[] args)
  {
      var container = new MyFavoriteContainerFromTheMarket();

      container.Register<IDependency>(new Dependency(/* I want this to come later */));

      container.Register<Dependent>(new Dependent(), 
       new ConstructorInjection<IDependency>());

      Console.Write("Hey, user. How are you feeling?");
      var userInput = Console.ReadLine();

      // It appears I can't do this as it makes perfect sense
      // to have the whole tree of dependencies known at the time
      // of registration.
      var dependent = container.Resolve<Dependent>(userInput);

      dependent.DoSomething();
  }
}

I don't know what that update makes of this previous discussion then. It just looks like some idiot (me) not having though out the question in his head before asking. Asking the question itself forced the right answer, which @Maarten confirmed and was trying to help me with.

Sorry, @Maarten. I wasted your time. :-)

¿Fue útil?

Solución

You shouldn't try to resolve an instance by calling the container. You should use constructor injection to inject your dependencies. Mark Seeman refers to this as Don't call the container, let it call you.

To solve your problem of creating an instance of an injected type with a run-time value, use abstract factories.

So your example can be solved like this:

class Dependency : IDependency
{
  public Dependency(string userInput) { }
}

interface IDependencyFactory {
    IDependency Create(string userInput);
}

class DependencyFactory: IDependencyFactory {
    IDependency Create(string userInput) {
        return new Dependency(userInput);
    }
}

class Dependent {
    public Dependent(IDependencyFactory factory) {
        // guard clause omitted
        _factory = factory
    }

    private readonly IDependencyFactory _factory;

    void DoSomething() {
        // Do not call the container, let it call you.
        // So no container usage here.
        //var container = ...

        Console.Write("Hey, user. How are you feeling?");
        var userInput = Console.ReadLine();

        var dependency = _factory.Create(userInput);
        // There you go!
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top