lundi 25 septembre 2017

Ninject error: More than one matching bindings are available

I'm messing around with Ninject and MVC and design partterns. In my simple application I have a structure like this:

(the abstract classes)

  • interface ICoffee
  • abstract class Coffee: ICoffee

(couple of classes)

  • public class Espresso : Coffee, ICoffee
  • public class Cappuccino: Coffee, ICoffee
  • public class Late: Coffee, ICoffee

(couple of decorator classes)

  • public class Milk : ICoffee
  • public class Chocolate: ICoffee

The decorator classes accept a parameter of type ICoffee , change some settings and then return it.

Then I have the following classes:

  public abstract class CoffeeStore : ICoffeeStore
{
    private readonly IProcessedOrderFactory processedOrderFactory;
    private readonly IDictionary<string, Func<ICoffee, ICoffee>> condimentsStrategies;

    public CoffeeStore(
        IProcessedOrderFactory processedOrderFactory,
        IDictionary<string, Func<ICoffee, ICoffee>> condimentsStrategies)
    {
        if (condimentsStrategies == null)
        {
            throw new ArgumentNullException(nameof(condimentsStrategies));
        }

        if (processedOrderFactory == null)
        {
            throw new ArgumentNullException(nameof(processedOrderFactory));
        }

        this.processedOrderFactory = processedOrderFactory;
        this.condimentsStrategies = condimentsStrategies;
    }

    public IProcessedOrder ProcessOrder(IOrder order)
    {
        ICoffee coffee;

        var coffeeType = order.SelectedCoffeeType;

        CoffeSizeType coffeeSize;
        Enum.TryParse(order.SelectedCoffeeSize, out coffeeSize);

        coffee = this.CreateCoffee(coffeeType, coffeeSize);

        foreach (var condimentAsString in order.SelectedCoffeeCodimentsList)
        {
            if (!this.condimentsStrategies.ContainsKey(condimentAsString))
            {
                throw new ArgumentException(condimentAsString);
            }

            //error occurs on the following line:
            coffee = this.condimentsStrategies[condimentAsString](coffee); 
        }

        var processedOrder = this.processedOrderFactory.CreateOrder(coffee);

        return processedOrder;
    }

    // abstract factory method
    protected abstract ICoffee CreateCoffee(string coffeeType, CoffeSizeType size);
}

and

 public class SofiaCoffeeStore : CoffeeStore
{
    private readonly IDictionary<string, Func<CoffeSizeType, ICoffee>> strategies;

    public SofiaCoffeeStore(
        IProcessedOrderFactory processedOrderFactory,
        IDictionary<string, Func<ICoffee, ICoffee>> condimentsStrategies,
        IDictionary<string, Func<CoffeSizeType, ICoffee>> strategies)
        : base(processedOrderFactory, condimentsStrategies)
    {
        if (strategies == null)
        {
            throw new ArgumentNullException(nameof(strategies));
        }

        this.strategies = strategies;
    }

    protected override ICoffee CreateCoffee(string coffeeType, CoffeSizeType size)
    {
        if (!this.strategies.ContainsKey(coffeeType))
        {
            throw new ArgumentNullException();
        }

        return this.strategies[coffeeType](size);
    }
}

also the ninject registering module looks like this:

private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind(x => x
         .FromAssemblyContaining<ICoffee>()
         .SelectAllClasses()
         .Excluding<ICoffee>()
         .BindAllInterfaces());

        kernel.Bind(x => x
        .FromAssemblyContaining<ICoffee>()
        .SelectAllInterfaces()
        .EndingWith("Factory")
        .BindToFactory());

        kernel.Bind<ICoffee>().To<Americano>().NamedLikeFactoryMethod((ICoffeeTypeFactory f) => f.GetAmericano(default(CoffeSizeType)));
        kernel.Bind<ICoffee>().To<Cappuccino>().NamedLikeFactoryMethod((ICoffeeTypeFactory f) => f.GetCappucino(default(CoffeSizeType)));
        kernel.Bind<ICoffee>().To<Espresso>().NamedLikeFactoryMethod((ICoffeeTypeFactory f) => f.GetEspresso(default(CoffeSizeType)));
        kernel.Bind<ICoffee>().To<Latte>().NamedLikeFactoryMethod((ICoffeeTypeFactory f) => f.GetLatte(default(CoffeSizeType)));
        kernel.Bind<ICoffee>().To<Ristretto>().NamedLikeFactoryMethod((IPlovdivStoreCoffeeTypeFactory f) => f.GetRistretto(default(CoffeSizeType)));
        kernel.Bind<ICoffee>().To<Doppio>().NamedLikeFactoryMethod((ISofiaStoreCoffeeTypeFactory f) => f.GetDoppio(default(CoffeSizeType)));

        kernel.Bind<ICoffee>().To<Caramel>().NamedLikeFactoryMethod((ICondimentsFactory f) => f.GetCaramel(default(ICoffee)));
        kernel.Bind<ICoffee>().To<Chocolate>().NamedLikeFactoryMethod((ICondimentsFactory f) => f.GetChocolate(default(ICoffee)));
        kernel.Bind<ICoffee>().To<Cinnamon>().NamedLikeFactoryMethod((ICondimentsFactory f) => f.GetCinnamon(default(ICoffee)));
        kernel.Bind<ICoffee>().To<Milk>().NamedLikeFactoryMethod((ICondimentsFactory f) => f.GetMilk(default(ICoffee)));
        kernel.Bind<ICoffee>().To<WhippedCream>().NamedLikeFactoryMethod((ICondimentsFactory f) => f.GetWhippedCream(default(ICoffee)));

        var sofiaStoreStrategies = new Dictionary<string, Func<CoffeSizeType, ICoffee>>
        {
            { "Americano", kernel.Get<ICoffeeTypeFactory>().GetAmericano },
            { "Capuccino", kernel.Get<ICoffeeTypeFactory>().GetCappucino },
            { "Espresso", kernel.Get<ICoffeeTypeFactory>().GetEspresso },
            { "Latte", kernel.Get<ICoffeeTypeFactory>().GetLatte },
            { "Doppio", kernel.Get<ISofiaStoreCoffeeTypeFactory>().GetDoppio },
        };

        var plovdivStoreStrategies = new Dictionary<string, Func<CoffeSizeType, ICoffee>>
        {
            { "Americano", kernel.Get<ICoffeeTypeFactory>().GetAmericano },
            { "Capuccino", kernel.Get<ICoffeeTypeFactory>().GetCappucino },
            { "Espresso", kernel.Get<ICoffeeTypeFactory>().GetEspresso },
            { "Latte", kernel.Get<ICoffeeTypeFactory>().GetLatte },
            { "Ristretto", kernel.Get<IPlovdivStoreCoffeeTypeFactory>().GetRistretto },
        };

        var condimentsStrategies = new Dictionary<string, Func<ICoffee, ICoffee>>
        {
            { "Milk", kernel.Get<ICondimentsFactory>().GetMilk },
            { "Caramel", kernel.Get<ICondimentsFactory>().GetCaramel },
            { "Chocolate", kernel.Get<ICondimentsFactory>().GetChocolate },
            { "Cinnamon", kernel.Get<ICondimentsFactory>().GetCinnamon },
            { "Whipped cream", kernel.Get<ICondimentsFactory>().GetWhippedCream },
        };


        kernel.Bind<IDictionary<string, Func<CoffeSizeType, ICoffee>>>().ToConstant(sofiaStoreStrategies)
            .WhenInjectedInto<SofiaCoffeeStore>();
        kernel.Bind<IDictionary<string, Func<CoffeSizeType, ICoffee>>>().ToConstant(plovdivStoreStrategies)
            .WhenInjectedInto<PlovdivCoffeeStore>();
        kernel.Bind<IDictionary<string, Func<ICoffee, ICoffee>>>().ToConstant(condimentsStrategies)
                .WhenInjectedInto<CoffeeStore>();

       //some more bellow
    }

An error occurs in method ProcessOrder (class CoffeeStore).

Ninject.ActivationException occurred
HResult=0x80131500
Message=Error activating ICoffee
More than one matching bindings are available.
Matching bindings:
1) binding from ICoffee to Caramel
2) binding from ICoffee to Chocolate
3) binding from ICoffee to Cinnamon
 .....
22) binding from ICoffee to WhippedCream
Activation path:
2) Injection of dependency ICoffee into parameter coffee of constructor of type Cinnamon
1) Request for ICoffee
Suggestions:
1) Ensure that you have defined a binding for ICoffee only once.

I'm collecting user input as string from 3-stage wizard form and then use it at once for object initialization. Hope the code I shared is enough to get an idea what i'm trying to do next: First I want to initialize one of the coffee type classes and then to decorate the object with one or more of the decorator classes. The error occurs on the second step - when I try to initialize a decorator class, the first step goes ok. Any idea how to overcome this ?

Aucun commentaire:

Enregistrer un commentaire