Соглашения Ninject с расширением Ninject Factory для привязки нескольких типов к одному интерфейсу

Я пытаюсь расширить сценарий, заданный в вопросе SO под названием Расширение Ninject Factory, связывающее несколько конкретных типов с одним интерфейсом, использование соглашений Ninject для основанной на соглашениях привязки реализаций ICar.

Я работаю над принятым ответом, автором которого является Akim и его Суть с изложением полного примера.

Разница в том, что я заменил явные привязки ICar привязками, основанными на соглашении (или, по крайней мере, попыткой сделать это;)

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

        // my unsuccessful binding
        Kernel.Bind(scanner => scanner
                        .FromThisAssembly()
                        .SelectAllClasses()
                        .InheritedFrom<ICar>()
                        .BindAllInterfaces());
        //Bind<ICar>()
        //    .To<Mercedes>()
        //    .Named("Mercedes");
        //Bind<ICar>()
        //    .To<Ferrari>()
        //    .Named("Ferrari");
    }
}

Когда я пытаюсь создать экземпляр переменной car в тесте, я получаю ActivationException:

Ninject.ActivationException was unhandled by user code
  Message=Error activating ICar
No matching bindings are available, and the type is not self-bindable.
Activation path:
  1) Request for ICar

Suggestions:
  1) Ensure that you have defined a binding for ICar.
  2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
  3) Ensure you have not accidentally created more than one kernel.
  4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
  5) If you are using automatic module loading, ensure the search path and filters are correct.

  Source=Ninject
  StackTrace:
       at Ninject.KernelBase.Resolve(IRequest request) in c:\Projects\Ninject\ninject\src\Ninject\KernelBase.cs:line 362
       at Ninject.ResolutionExtensions.GetResolutionIterator(IResolutionRoot root, Type service, Func`2 constraint, IEnumerable`1 parameters, Boolean isOptional, Boolean isUnique) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:line 263
       at Ninject.ResolutionExtensions.Get(IResolutionRoot root, Type service, String name, IParameter[] parameters) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:line 164
       at Ninject.Extensions.Factory.Factory.InstanceResolver.Get(Type type, String name, Func`2 constraint, ConstructorArgument[] constructorArguments, Boolean fallback) in c:\Projects\Ninject\ninject.extensions.factory\src\Ninject.Extensions.Factory\Factory\InstanceResolver.cs:line 75
       at Ninject.Extensions.Factory.StandardInstanceProvider.GetInstance(IInstanceResolver instanceResolver, MethodInfo methodInfo, Object[] arguments) in c:\Projects\Ninject\ninject.extensions.factory\src\Ninject.Extensions.Factory\Factory\StandardInstanceProvider.cs:line 78
       at Ninject.Extensions.Factory.FactoryInterceptor.Intercept(IInvocation invocation) in c:\Projects\Ninject\ninject.extensions.factory\src\Ninject.Extensions.Factory\Factory\FactoryInterceptor.cs:line 57
       at Castle.DynamicProxy.AbstractInvocation.Proceed()
       at Castle.Proxies.ICarFactoryProxy.CreateCar(String carType)
       at Ninject.Extensions.Conventions.Tests.NinjectFactoryTests.A_Car_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument() in C:\Programming\Ninject.Extensions.Conventions.Tests\NinjectFactoryTests.cs:line 33
  InnerException: 

Как я могу пройти этот тест?

[Fact]
public void A_Car_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument()
{
    // auto-module loading is picking up my CarModule - otherwise, use:
    // using (StandardKernel kernel = new StandardKernel(new CarModule()))
    using (StandardKernel kernel = new StandardKernel())
    {
        // arrange
        string carTypeArgument = "Mercedes";
        ICarFactory factory = kernel.Get<ICarFactory>();

        // act
        var car = factory.CreateCar(carTypeArgument);

        // assert
        Assert.Equal(carTypeArgument, car.GetType().Name);
    }
}

Вот остальная часть кода, максимально сжатая, чтобы вам не приходилось обращаться к исходному вопросу

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

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

public class Mercedes : ICar {
    public void Drive() { /* mercedes drives */ }
    public void Stop() { /* mercedes stops */ }
}

public class Ferrari : ICar {
    public void Drive() { /* ferrari drives */ }
    public void Stop() { /* ferrari stops */ }
}

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

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



Ответы (1)


Похоже, вам нужно определить привязку по-другому и предоставить собственную реализацию IBindingGenerator для этого случая.

Связывание

Вся реализация ICar будет иметь пользовательскую привязку

Kernel.Bind(scanner => scanner
                            .FromThisAssembly()
                            .SelectAllClasses()
                            .InheritedFrom<ICar>()
                            .BindWith(new BaseTypeBindingGenerator<ICar>()));

Пользовательская IBindingGenerator реализация

Поиск всех реализаций интерфейса и привязка их по имени типа

public class BaseTypeBindingGenerator<InterfaceType> : IBindingGenerator
{
    public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
    {
        if (type != null && !type.IsAbstract && type.IsClass && typeof(InterfaceType).IsAssignableFrom(type))
        {          
            yield return bindingRoot.Bind(typeof(InterfaceType))
                                    .To(type)
                                    .Named(type.Name) as IBindingWhenInNamedWithOrOnSyntax<object>;
        }
    }

PS: вот полный пример

person Akim    schedule 08.04.2013
comment
Это работает, спасибо! Я озадачен тем, почему все эти вращения необходимы для моего, казалось бы, простого варианта использования. У вас есть комментарии по этому поводу? (Неужели это не такой простой пример использования привязок) - person Jeff; 08.04.2013
comment
могут быть более простые решения для соглашений, если вы не используете именованную привязку - person Akim; 09.04.2013