2

我建立在先前回答questionICar實現使用Ninject Conventions Extensions和一個自定義IBindingGeneratorICarFactory接口,其必然是使用Ninject Factory Extensions'ToFactory()方法和custom instance provider約束。是否可以將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); 
    } 
} 

}

A 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類,因爲我明確地自我約束它。你能發現我做錯了什麼嗎?

回答

2

使用泛型方法:

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

好了,所以這[不會真的是一個抽象工廠(http://blog.ploeh.dk/2010/11/01/PatternRecognitionAbstractFactoryorServiceLocator/)然後,因爲泛型論證是在方法中而不是在工廠中?或者它仍然是一個抽象工廠,因爲內核是抽象工廠?試圖瞭解您的解決方案是否正確,因爲我的設計存在缺陷,或者僅僅是因爲這是在這種情況下常用的方法? – Jeff

+0

請參閱我的編輯與您的建議 - 測試尚未通過,因爲它不能解決我提供給'CreateVehicle ',這是'梅賽德斯',在這種情況下特定的'ICAR'類。我不知道爲什麼,因爲我甚至明確地自我約束它。你能看到我做錯了什麼嗎?它似乎並不像切換到一般方法那麼簡單。 – Jeff

+0

您的UseFirstGenericTypeArgumentInstanceProvider強制綁定被命名。將其刪除後添加.named(「Mercedes」)到您的綁定。 –

相關問題