4
votes

I need to create objects sharing common interface (IFoo) based on a string I get from the database. I have "A", I need to intantiate AFoo, I get "B", I need to produce BFoo, etc. The first thing I tought of was a factory. But the objects created (AFoo, BFoo) need to have their dependencies injected (and those dependencies need more dependencies and some even arguments). For all the injecting I use Ninject, which by itself seems to be a fancy factory. To create objects within my factory I inject a Ninject's kernel via constructor. Is that the desired way?

interface IBar { }

class Bar : IBar {
    public Bar(string logFilePath) { }
}

interface IFoo { }

class AFoo : IFoo {
    public AFoo(IBar bar) { }
}

class BFoo : IFoo { }

class FooFactory : IFooFactory { 
    private IKernel _ninjectKernel;

    public FooFactory(IKernel ninjectKernel) {
        _ninjectKernel = ninjectKernel;
    }

    IFoo GetFooByName(string name) {
          switch (name) {
               case "A": _ninjectKernel.Get<AFoo>();
          }
          throw new NotSupportedException("Blabla");
    }
}

class FooManager : IFooManager {
    private IFooFactory _fooFactory;

    public FooManager(IFooFactory fooFactory) {
        _fooFactory = fooFactory;
    }

    void DoNastyFooThings(string text) {
        IFoo foo = _fooFactory.GetFooByName(text);
        /* use foo... */
    }
}

class Program {
    public static void Main() {
        IKernel kernel = new StandardKernel();
        kernel.Bind<IBar>.To<Bar>();
        kernel.Bind<IFooManager>.To<FooManager>();
        kernel.Bind<IFooFactory>.To<FooFactory>();
        IFooManager manager = kernel.Get<IFooManager>(new ConstructorArgument("ninjectKernel", kernel, true));
        manager.DoNastyFooThings("A");
    }
}
1

1 Answers

7
votes

Ninject's IKernel's Get<T>() method has an overload which takes an name argument to get a named instance.

The usage would be:

public int Main()
{
    IKernel kernel = new StandardKernel();

    kernel.Bind<IFoo>().To<AFoo>().Named("AFoo");
    kernel.Bind<IFoo>().To<BFoo>().Named("BFoo");

    //returns an AFoo instance
    var afoo = kernel.Get<IFoo>("AFoo");

    //returns an BFoo instance
    var bfoo = kernel.Get<IFoo>("BFoo"); 
}

Regarding your question about injecting Ninject's IKernel into the Factory's constructor, I don't think there should be any problems. Your factory should look like this:

public interface IFooFactory
{
    IFoo GetFooByName(string name);
}

public class FooFactory : IFooFactory
{
    private readonly IKernel _kernel;

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

    public IFoo GetFooByName(string name)
    {
        return _kernel.Get<IFoo>(name);
    }
}

Also you could add a binding to IKernel like this:

kernel.Bind<IKernel>().ToConstant(kernel);