Search code examples
c#dependency-injectionninject

Creating objects based on a string using Ninject


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");
    }
}

Solution

  • 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);