views:

73

answers:

3

Hi,

I'm just getting started with dependency injection. I've read the Ninject wiki and its very clear on how to inject dependencies where a single instance of the dependency is required, using constructor, property or method injection. But how do you handle the case where your class needs to construct objects during its lifetime (after construction)? For example:

class AddressBook
{
    private List<IContact> _contacts;

    public AddContact(string name)
    {
        _contacts.Add(****what?****)
    }
}

The only way I can think is to use constructor injection to pass in an IKernel and use that to get our IContact:

class AddressBook
{
    private IKernel _kernel;
    private List<IContact> _contacts;

    public AddressBook(IKernel kernel){ _kernel = kernel; }        

    public AddContact(string name)
    {
        _contacts.Add(_kernel.Get<IContact>(new Parameter("name", name)));
    }
}

But then how can you actually inject the kernel? What mapping would be required? Is this even the right approach?

Thanks for any help felix

+1  A: 

The answer as suggested by Benjamin Podszun:

Inject a factory:

public interface IContactFactory
{
    IContact CreateContact(string name);
}

class AddressBook
{
    private IContactFactory _factory;
    private List<IContact> _contacts;

    public AddressBook(IContactFactory factory){ _factory = factory; }        

    public AddContact(string name)
    {
        _contacts.Add(_factory.CreateContact(name));
    }
}

Then you can bind the factory to whatever you want to create any specific instance of IContact.

felix
Yes, that's how I would do it. http://kozmic.pl/archive/2010/06/22/how-i-use-inversion-of-control-containers-ndash-pulling-from.aspx
Krzysztof Koźmic
A: 

You can do it pretty cleanly with: (exec summary of another answer re a slightly different question)

Bind<Func<IContact>>().ToMethod( context => () => Kernel.Get<Contact>() );

Your other options are:

  • have an IKernel injected as you did (that's supported OOTB with any special tricks), but as you allude to, this is rarely what you want - that's tantamount to Service Location.

  • Do a full-scale factory. See the other answer for the idiomatic Ninject way (a provider) to do more or less what your answer-to-self says. You better have a good reason to do that amount of boiler plate though.

Ruben Bartelink
+1  A: 

Similar to what the others have answered, we use a generic IFactory interface:

public interface IFactory<T> 
{
    T Get();
}

Which can be used like this:

public AddressBook(IFactory<IContact> ContactFactory)

And then implemented like this:

public class InjectorFactory : IFactory<T> 
{
    // we wrapped the Kernel in an Injector class
    public T Get() { return Injector.Get<T>(); } 
}

And bound like this:

Bind(typeof(IFactory<>)).To(typeof(InjectorFactory<>))

It has worked very well for us so far.

StriplingWarrior
+1 Nice. I'm pretty sure doing a `Bind(typeof(Func<>)).ToMethod(context=> ()=> context.Get(context.Service))` would work too (though I'd probably go with yours as it's nice and explicit). Also, if people are interested, there's some tests/samples in the source re binding open generics like this.
Ruben Bartelink
Yeah, binding a Func would let you skip the interface, and can make testing easier. We ended up implementing a `FunctionBasedFactory` to make it so we could just use a Func to inject factories in unit tests. But I do prefer using an interface for dependencies.
StriplingWarrior