Можно ли использовать метод 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>();
}