Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ninject Factory Extension Bind Multiple Concrete Types To One Interface

Introduction:

I'm using the Ninject Factory Extension to inject simple object factories into my service classes.

Here is my interface and two classes that implement it:

public interface ICar
{
    void Drive();
    void Stop();
}

public class Mercedes : ICar
{
    public void Drive()
    {
      Do mercedes drive stuff...
    }

    public void Stop()
    {
      Do mercedes stop stuff...  
    }

}

public class Ferrari : ICar
{
    public void Drive()
    {
      Do ferrari drive stuff...
    }

    public void Stop()
    {
      Do ferrari stop stuff...  
    }
}

Here is my object factory to dynamically create a car at runtime:

public interface ICarFactory
{
   ICar CreateCar(string carType);
}

public class CarFactory : ICarFactory 
{
   public ICar CreateCar(string carType)
   {
       ICar car;

       switch (type)
       {
           case "mercedes":
                car = new Mercedes();
           break;

           case "ferrari":
               car = new Ferrari();
           break;
       }

       return car;
    }
 }

Then use ninject factory extension "ToFactory" method to bind my car factory interface:

public class CarModule : Ninject.Modules.NinjectModule
{
      public override void Load()
      {
           Bind<ICarFactory>().ToFactory();
      }
}

Problem:

My factory gets injected into my service class as expected and can be used to create car objects, however ninject blows up here because it cannot properly resolve ICar to the concrete type ie. Mercedes or Ferrari returned by the factory CreateCar() method.

public CarService(string carType, ICarFactory carFactory)
{
   var car = carFactory.CreateCar(carType);
}

Question:

Assuming the factory pattern I'm using here is compatible with how ninject factory extension is intended to work, how do I setup the bindings for ICar -> Ferrari, ICar -> Mercedes etc. so that they may be dynamically resolved at run time with this approach?

Thanks!

like image 227
mmacneil007 Avatar asked Jan 22 '13 03:01

mmacneil007


1 Answers

There is an example of custom factory at ninject.extension.factory wiki

First, create custom implementation of StandardInstanceProvider to override default factory behaviour

public class UseFirstArgumentAsNameInstanceProvider : StandardInstanceProvider
{
    protected override string GetName(System.Reflection.MethodInfo methodInfo, object[] arguments)
    {
        return (string)arguments[0];
    }

    protected override ConstructorArgument[] GetConstructorArguments(MethodInfo methodInfo, object[] arguments)
    {
        return base.GetConstructorArguments(methodInfo, arguments).Skip(1).ToArray();
    }
}

At CarModule specify UseFirstArgumentAsNameInstanceProvider (custom instance provider) for ICarFactory factory and names for dependencies

public class CarModule : NinjectModule
{
    public override void Load()
    {
        Bind<ICarFactory>()
            .ToFactory(() => new UseFirstArgumentAsNameInstanceProvider());

        Bind<ICar>()
            .To<Mercedes>()
            .Named("Mercedes");

        Bind<ICar>()
            .To<Ferrari>()
            .Named("Ferrari");
    }
}

Resolve factory and dependencies

var factory = kernel.Get<ICarFactory>();

var mercedes = factory.CreateCar("Mercedes");
var ferrari = factory.CreateCar("Ferrari");

ps: here is full example

like image 177
Akim Avatar answered Oct 03 '22 01:10

Akim