【问题标题】:Is it possible to use Ninject Factory Extensions' ToFactory method with open generics?是否可以将 Ninject Factory Extensions 的 ToFactory 方法与开放泛型一起使用?
【发布时间】:2013-04-08 23:54:19
【问题描述】:

我建立在先前回答的question 的基础上,其中ICar 实现使用Ninject Conventions Extensions 和自定义IBindingGenerator 绑定,ICarFactory 接口使用Ninject Factory Extensions' ToFactory() 方法和@ 绑定987654325@.

我正在尝试重构,以便可以绑定和使用IVehicleFactory<T>,其中T 被限制为ICar,而不是之前的ICarFactory。这样,我可以在泛型类型参数中指定我想要的车辆,而不是在工厂的CreateCar()方法中传入车辆类型的名称。

是否可以使用ToFactory() 技术绑定开放的通用接口?

我感觉我找错了树,但是当我通过名称指定 ICar 类型时,将 ICar 类型本身指定为泛型类型参数似乎是自然演变...

这是当前失败的测试:

[Fact]
public void A_Generic_Vehicle_Factory_Creates_A_Car_Whose_Type_Equals_Factory_Method_Generic_Type_Argument()
{
    using (StandardKernel kernel = new StandardKernel())
    {
        // arrange
        kernel.Bind(typeof(IVehicleFactory<>))
            .ToFactory(() => new UseFirstGenericTypeArgumentInstanceProvider());

        kernel.Bind(
            scanner => scanner
                           .FromThisAssembly()
                           .SelectAllClasses()
                           .InheritedFrom<ICar>()
                           .BindWith(new BaseTypeBindingGenerator<ICar>()));
        IVehicleFactory<Mercedes> factory 
            = kernel.Get<IVehicleFactory<Mercedes>>();

        // act
        var car = factory.CreateVehicle();

        // assert
        Assert.IsType<Mercedes>(car);
    }
}

还有InvalidCastException 抛出:

System.InvalidCastException was unhandled by user code
  Message=Unable to cast object of type 'Castle.Proxies.ObjectProxy' to type 'IVehicleFactory`1[Mercedes]'.
  Source=System.Core
  StackTrace:
       at System.Linq.Enumerable.<CastIterator>d__b1`1.MoveNext()
       at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
       at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:line 37
       at NinjectFactoryTests.A_Generic_Vehicle_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument() in C:\Programming\Ninject.Extensions.Conventions.Tests\NinjectFactoryTests.cs:line 37
  InnerException: 

还有工厂接口:

public interface IVehicleFactory<T> where T : ICar
{
    T CreateVehicle();
}

还有自定义实例提供程序,我什至无法让调试器停止其断点,所以我真的不知道那里发生了什么:

public class UseFirstGenericTypeArgumentInstanceProvider : StandardInstanceProvider
{
    protected override string GetName(MethodInfo methodInfo, object[] arguments)
    {
        var genericTypeArguments = methodInfo.GetGenericArguments();
        var genericMethodDefinition = methodInfo.GetGenericMethodDefinition();
        var g = genericMethodDefinition.MakeGenericMethod(genericTypeArguments.First());
        return g.MemberType.GetType().Name;
    }

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

EDIT 1 - Change IVehicleFactory 签名和自定义实例提供者

这里我已将IVehicleFactory 签名更改为使用通用Create&lt;T&gt;() 方法,并将Mercedes 显式绑定到自身。

public interface IVehicleFactory
{
    T CreateVehicle<T>() where T : ICar;
}

以及返回第一个泛型类型参数名称的新自定义实例提供程序:

public class UseFirstGenericTypeArgumentInstanceProvider : StandardInstanceProvider
{
    protected override string GetName(MethodInfo methodInfo, object[] arguments)
    {
        var genericTypeArguments = methodInfo.GetGenericArguments();
        return genericTypeArguments[0].Name;
    }
}

这是新的测试,仍未通过:

[Fact]
public void A_Generic_Vehicle_Factory_Creates_A_Car_Whose_Type_Name_Equals_Factory_Method_String_Argument()
{
    using (StandardKernel kernel = new StandardKernel())
    {
        // arrange
        kernel.Bind<IVehicleFactory>()
            .ToFactory(() => new UseFirstGenericTypeArgumentInstanceProvider())
            .InSingletonScope();
        kernel.Bind<Mercedes>().ToSelf();
        IVehicleFactory factory = kernel.Get<IVehicleFactory>();

        // act
        var car = factory.CreateVehicle<Mercedes>();

        // assert
        Assert.IsType<Mercedes>(car);
    }
}

}

Ninject.ActivationException 被抛出:

Ninject.ActivationException: Error activating Mercedes
No matching bindings are available, and the type is not self-bindable.
Activation path:
  1) Request for Mercedes

我不知道为什么它找不到Mercedes 类,因为我明确地自我绑定了它。你能发现我做错了什么吗?

【问题讨论】:

    标签: ninject ioc-container ninject-extensions abstract-factory open-generics


    【解决方案1】:

    使用泛型方法:

    public interface IVehicleFactory
    {
        CreateVehicle<T>();
    }
    

    【讨论】:

    • 好的,那么这个wouldn't really be an abstract factory,因为泛型参数在方法中而不是在工厂中?或者它仍然是一个抽象工厂,因为内核是抽象工厂?只是想了解您的解决方案是否正确是因为我的设计存在缺陷,还是仅仅因为在这种情况下通常这样做?
    • 请查看我对您的建议的编辑 - 测试尚未通过,因为它无法解析我提供给 CreateVehicle&lt;T&gt; 的特定 ICar 类,即 Mercedes,在此案子。我不知道为什么,因为我什至明确地对它进行了自我约束。你能看出我做错了什么吗?看起来不像切换到泛型方法那么简单。
    • 您的 UseFirstGenericTypeArgumentInstanceProvider 强制绑定已命名。要么删除它,要么将 .Named("Mercedes") 添加到您的绑定中。
    • 我明白了 - 是我对自定义实例提供程序的实现强制执行命名绑定,还是使用 StandardInstanceProvider 的任何实现强制执行命名绑定?如果我想使用命名绑定以外的方式进行绑定,我应该追求什么路径?我想设置一些最终可以与基于约定的绑定一起使用的东西,所以添加.Named("Mercedes") 是不切实际的。
    猜你喜欢
    • 2021-08-09
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    相关资源
    最近更新 更多