Можно ли использовать метод ToFactory от Ninject Factory Extensions с открытыми обобщениями?

Я опираюсь на ранее отвеченный вопрос, в котором ICar реализации связаны с использованием расширений Ninject Conventions и пользовательского IBindingGenerator, а также ICarFactory интерфейс связан с использованием Ninject Factory Extensions ToFactory() метод и пользовательский поставщик экземпляров.

Я пытаюсь рефакторинг, чтобы я мог связать и использовать 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();
    }
}

РЕДАКТИРОВАТЬ 1 - Изменить IVehicleFactory поставщик подписи и пользовательского экземпляра

Вот я изменил IVehicleFactory подпись для использования общего Create<T>() метод и явно связаны 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 класс, так как я явно сам связал его. Можете ли вы определить, что я делаю не так?

1 ответ

Используйте универсальные методы:

public interface IVehicleFactory
{
    CreateVehicle<T>();
}
Другие вопросы по тегам