【问题标题】:Ninject Conventions with Ninject Factory Extension To Bind Multiple Types To One InterfaceNinject 约定与 Ninject 工厂扩展将多种类型绑定到一个接口
【发布时间】:2023-08-24 23:53:02
【问题描述】:

我正在尝试通过使用 Ninject Conventions 对 ICar 实现进行基于约定的绑定来扩展标题为 Ninject Factory Extension Bind Multiple Concrete Types To One Interface 的 SO 问题中提出的场景。

我正在处理 the accepted answer authored by Akimhis Gist 概述完整示例。

不同之处在于我已经用基于约定的绑定替换了显式的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);
    }
}

这里是剩下的代码,尽量精简,这样就不用参考original question

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

【问题讨论】:

    标签: inversion-of-control factory ninject-extensions ninject-3 ninject-conventions


    【解决方案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:这里是full sample

    【讨论】:

    • 行得通,谢谢!我很困惑为什么所有这些回转对于我看似简单的用例工作来说都是必要的。你有任何关于它的cmets吗? (这真的不是那么简单的绑定用例示例吗)
    • 可能有更简单的约定解决方案,除非您不使用命名绑定