Inject right object depending on method parameters at ninject factory namebinding

103 Views Asked by At

I use ninject and ninject factory extension as follows:

I created two interfaces, the first interface is for the factory and the second is for the concrete class to be injected depending on the arguments at the factory method.

The factory interface:

public enum VehicleCategory
{
  Bus,
  Car,
  Truck
}

public interface IVehicleType
{
  IVehicleMethods GetVehicleType(VehicleCategory vehicleCategory, bool newVehicle);
}

The class interface:

public interface IVehicleInfo
{
  string GetVehicleInformation();
}

The ninject binding:

Bind<IVehicleType>().ToFactory();

Bind<IVehicleInfo>().To<TruckVehicle>().NamedLikeFactoryMethod((IVehicleType f) =>
    f.GetVehicleType(VehicleCategory.Truck, false);
Bind<IVehicleInfo>().To<CarVehicle>().NamedLikeFactoryMethod((IVehicleType f) =>
    f.GetVehicleType(VehicleCategory.Car, false);
Bind<IVehicleInfo>().To<BusVehicle>().NamedLikeFactoryMethod((IVehicleType f) =>
    f.GetVehicleType(VehicleCategory.Bus, false);

The inject call:

var kernel = new StandardKernel(new NinjectModule());
var vehicles = kernel.Get<IVehicleType>();
var truck = vehicles.GetVehicleType(VehicleCategory.Truck, false);
truck.GetVehicleInformation();

Currently I get the error "More than one matching binging are available", so I assume the method parameters does not matter. What should I change so that the correct object is created/injected depending on the method parameters?

1

There are 1 best solutions below

0
On

You may try something like this :

using System;
using System.Linq;
using System.Reflection;

using Ninject.Extensions.Factory;
using Ninject.Parameters;

public class ResolveFromEnum<T> : StandardInstanceProvider
    where T : struct, IConvertible
{
    static ResolveFromEnum()
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(string.Format("{0} must be an enumerated type", typeof(T).Name));
        }
    }

    protected override string GetName(System.Reflection.MethodInfo methodInfo, object[] arguments)
    {
        return ((T)arguments[0]).ToString();
    }

    protected override IConstructorArgument[] GetConstructorArguments(MethodInfo methodInfo, object[] arguments)
    {
        var relevantParameters = methodInfo.GetParameters().Skip(1).ToList();
        var relevantArguments = arguments.Skip(1).ToList();

        IConstructorArgument[] result = new IConstructorArgument[relevantParameters.Count];

        for (int i = 0; i < relevantParameters.Count; i++)
        {
            var closure = i;
            result[i] = new TypeMatchingConstructorArgument(relevantParameters[i].ParameterType, (context, target) => relevantArguments[closure], true);
        }
        return result;
    }
}

then

Bind<IVehicleType>().ToFactory(() => new ResolveFromEnum<VehicleCategory>());

and

Bind<IVehicleInfo>().To<TruckVehicle>().Named(VehicleCategory.Truck.ToString());
Bind<IVehicleInfo>().To<CarVehicle>().Named(VehicleCategory.Car.ToString());
Bind<IVehicleInfo>().To<BusVehicle>().Named(VehicleCategory.Bus.ToString());

and this should not change

var kernel = new StandardKernel(new NinjectModule());
var vehiclesFactory = kernel.Get<IVehicleType>();
var truck = vehiclesFactory.GetVehicleType(VehicleCategory.Truck, false);
truck.GetVehicleInformation();