问题
I'm trying to expand on the scenario asked in the SO question titled Ninject Factory Extension Bind Multiple Concrete Types To One Interface by using Ninject Conventions for convention-based binding of the ICar
implementations.
I'm working off the accepted answer authored by Akim and his Gist outlining the full example.
The difference is that I've replaced the explicit ICar
bindings with convention-based bindings (or an attempt at it, at least ;)
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");
}
}
When I attempt to instantiate the car
variable in the test, I get an 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:
How can I get this test to pass?
[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);
}
}
Here's the rest of the code, as condensed as possible, so that you don't have to refer to the 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();
}
}
回答1:
Looks like, you have to define binding differently and provide your custom implementation of IBindingGenerator
for this case
Binding
All implementation of ICar
will have custom binding
Kernel.Bind(scanner => scanner
.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<ICar>()
.BindWith(new BaseTypeBindingGenerator<ICar>()));
Custom IBindingGenerator
implementation
Searching for all implementations of interface and bind them by type name
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: here is a full sample
来源:https://stackoverflow.com/questions/15868820/ninject-conventions-with-ninject-factory-extension-to-bind-multiple-types-to-one