views:

133

answers:

1

I need a little more help to "get" how a DI framework like Ninject moves past the basics.

Take the Ninject sample:

class Samurai {
private IWeapon _weapon;

    [Inject]
    public Samurai(IWeapon weapon) {
      _weapon = weapon;
    }

    public void Attack(string target) {
      _weapon.Hit(target);
    }
 }

Without a DI framework (i.e. the [Inject] references above) a referencing class would look something like:

class Program {
   public static void Main() { 
    Samurai warrior1 = new Samurai(new Shuriken());
    Samurai warrior2 = new Samurai(new Sword());
    warrior1.Attack("the evildoers");
    warrior2.Attack("the evildoers");
  }
}

...where you're newing up everything. Yes, you've removed the dependency in Samurai, but now you've got a dependency one step further up the chain. Simple.

With Ninject, you get rid of newing up everything by:

class Program {
  public static void Main() {
    IKernel kernel = new StandardKernel(new WarriorModule());
    Samurai warrior = kernel.Get<Samurai>();
    warrior.Attack("the evildoers");
  }
}

HOWEVER, this is my area of confusion: without making some kind of service locater to take care of efficiently newing up the applicable kernel and module (i.e. .IoC.TypeResolver.Get<>()), what is a) the best way to not have to new up kernels everywhere (service locater references everywhere?), and b) more importantly, when you've got a big long chain with dependencies with dependencies of their own, you take it to the extreme of passing injections all the way up in something that I'm sure is a serious anti-pattern (a friend called it "hot-potato dependency injection" anti-pattern).

In other words, I thought part of the DI framework magic is that you don't have to keep inserting dependencies all the way up the chain (i.e. your your first reference containing 10 parameters in its constructor, none of which have anything to do with anything until much further along the chain) - where's the magic or the solution to my confusion so that dependencies don't either continually get referenced up and up the chain, or service locater references spread everywhere.

Further muddying the waters for me is if using a DI framework, what's the best way to deal with a scenario where a referenced class needs an IList which would typically be put in the constructor (i.e. new ReferencedClass(myList)) as that, other than in simple cases like a string database connection string, doesn't fly. Just make a property and set it after newing it up/DI Framework service locating? I.e.

var referencedClass = IoC.Get<IReferencedClass>();
referencedClass.MyList = myList;

All in all, I think this is a post I'll likely be embarrassed about after I get it, but right now, I've hit my head too many times against the wall trying to determine the best approach.

A: 

As for the "hot potato" dependency issue, this doesn't have to happen. The dependency injection framework handles this for you.

For instance, if Class1 has a dependency on Class2, and Class2 has a dependency on Class3, you do not need to inject a Class3 into Class1 in order to accommodate the Class2 dependency. The kernel will walk down the dependency chain for you and will resolve the down-stream dependencies automatically (as long as all of the classes in play have been registered in the kernel) when you ask for a Class1.

Class1 depends on Class2 depends on Class3

The Class1 constructor does not need to mention Class3 at all.

As for the second issue, how or if this is accommodated is dependent on the framework. With Ninject, I think you can use the Bind().To().WithConstructorArgument() syntax to provide the new List item to the constructor.

Eric King
1) Face slap. Don't know how my tests on this failed before, but sure enough, handles wiring things up down the chain without issue. 2) The WithConstructorArgument syntax has changed in Ninject v2, but I can't recall it offhand. Regardless, this requires that either your static gateway/service locater handles setting this, or you need to reference the kernel when you want to set it, or the argument needs to be known before runtime (i.e. database connection string). Therefore, I'm just setting as property which feels more fragile, but it works and it is in line with what I've read before.
Ted