当具体类型派生自抽象基类时,Ninject 无法解析接口类型
我在 Ninject 尝试解析接口类型时遇到问题,其中具体类型派生自实现该接口的抽象基类。
编辑:这是在使用 .NET CF 的 Windows Mobile 上进行的。
我的具体问题涉及演示者和视图,因此我在此示例中坚持使用它,而不是 foos 和 Bar。
我想为演示者和视图注入工厂,以便稍后在 UI 视图堆栈深处创建这些实例。
为了更好的可读性,下面我省略了所有错误检查。
我的工厂界面:
public interface IFactory<T>
{
T Create();
}
我的演示者和视图:
public sealed class Presenter
{
private readonly View view;
public Presenter(View view)
{
this.view = view;
}
}
public sealed class View
{
public View()
{
}
}
首先,我将展示什么工作完美,Ninject 按预期解析。这不包括我在开头提到的抽象基类。之后,我将添加对抽象基类的轻微修改,这将使 Ninject 在尝试解析依赖项时抛出异常。
我们在上面看到演示者依赖于视图,因此演示者工厂将依赖于视图工厂:
public sealed class GoodPresenterFactory : IFactory<Presenter>
{
private readonly IFactory<View> viewFactory;
public GoodPresenterFactory(IFactory<View> viewFactory)
{
this.viewFactory = viewFactory;
}
public Presenter Create()
{
return new Presenter(this.viewFactory.Create());
}
}
public sealed class ViewFactory : IFactory<View>
{
public ViewFactory()
{
}
public View Create()
{
return new View();
}
}
用 Ninject 将其连接起来:
Bind<IFactory<Presenter>>().To<GoodPresenterFactory>();
Bind<IFactory<View>>().To<ViewFactory>();
然后解析演示者工厂:
var presenterFactory = container.Get<IFactory<Presenter>>();
到目前为止一切都运行良好。演示者工厂内对视图工厂的依赖关系已按预期得到解决。
现在,我有一百万个类,看起来像上面的 GoodPresenterFactory,因此我想要一个小的基类来处理一些琐碎的常见内容,例如演示者工厂中对视图工厂的依赖:
public abstract class FactoryBase<T, U> : IFactory<T>
{
protected readonly U dependency;
protected FactoryBase(U dependency)
{
this.dependency = dependency;
}
public abstract T Create();
}
然后演示者工厂将发生变化,并且该变化中的某些内容将使 Ninject 解析失败:
public sealed class BadPresenterFactory : FactoryBase<Presenter, IFactory<View>>
{
public BadPresenterFactory(IFactory<View> viewFactory)
: base(viewFactory)
{
}
public override Presenter Create()
{
return new Presenter(this.dependency.Create());
}
}
并相应地更改 Ninject 接线:
Bind<IFactory<Presenter>>().To<BadPresenterFactory>();
Bind<IFactory<View>>().To<ViewFactory>();
这些更改将使 Ninject 在
var presenterFactory = container.Get<IFactory<Presenter>>();
从异常执行调用堆栈时抛出 ArgumentNullException:
at System.Reflection.RuntimeMethodInfo.GetParentDefinition()
at System.Reflection.CustomAttribute.IsDefined(MemberInfo member, Type caType, Boolean inherit)
at System.Reflection.RuntimeMethodInfo.IsDefined(Type attributeType, Boolean inherit)
at System.Attribute.IsDefined(MemberInfo element, Type attributeType, Boolean inherit)
at System.Attribute.IsDefined(MemberInfo element, Type attributeType)
at Ninject.Infrastructure.Language.ExtensionsForMemberInfo.HasAttribute(MemberInfo member, Type type)
at Ninject.Selection.Heuristics.StandardInjectionHeuristic.ShouldInject(MemberInfo member)
at Ninject.Selection.Selector.<>c__DisplayClassa.<SelectMethodsForInjection>b__9(IInjectionHeuristic h)
at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate)
at Ninject.Selection.Selector.<SelectMethodsForInjection>b__8(MethodInfo m)
at System.Linq.Enumerable.<WhereIterator>d__0`1.MoveNext()
at Ninject.Planning.Strategies.MethodReflectionStrategy.Execute(IPlan plan)
at Ninject.Planning.Planner.<>c__DisplayClass2.<GetPlan>b__0(IPlanningStrategy s)
at Ninject.Infrastructure.Language.ExtensionsForIEnumerableOfT.Map[T](IEnumerable`1 series, Action`1 action)
at Ninject.Planning.Planner.GetPlan(Type type)
at Ninject.Activation.Providers.StandardProvider.Create(IContext context)
at Ninject.Activation.Context.Resolve()
at Ninject.KernelBase.<Resolve>b__4(IContext context)
at System.Linq.Enumerable.<SelectIterator>d__d`2.MoveNext()
at System.Linq.Enumerable.<CastIterator>d__b0`1.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters)
at NinjectTest.Program.Main()
如果我修改 FactoryBase
使其没有依赖性,它只是一个裸基类,则 Ninject 也失败。
public abstract class NakedFactoryBase<T> : IFactory<T>
{
protected NakedFactoryBase()
{
}
public abstract T Create();
}
public sealed class PointlessPresenterFactory : NakedFactoryBase<Presenter>
{
private readonly IFactory<View> viewFactory;
public PointlessPresenterFactory(IFactory<View> viewFactory)
{
this.viewFactory = viewFactory;
}
public override Presenter Create()
{
return new Presenter(this.viewFactory.Create());
}
}
正如您所看到的,失败的 PointlessPresenterFactory
与成功的 GoodPresenterFactory
相同,除了 GoodPresenterFactory 中的直接
,而不是在 IFactory
实现PointlessPresenterFactory
中使用的完全裸基类。
知道为什么使用工厂基类时 Ninject 无法解析吗?
I have a problem with Ninject trying to resolve an interface type where the concrete type derives from an abstract base class that implements the interface.
EDIT: This is on Windows Mobile using .NET CF.
My particular issue involves presenters and views, so I stick to that in this example instead of foos and bars.
I want to inject factories for presenters and views to allow for late creation of those instances deep down the UI view stack.
Below I've omitted all error checking for better readability.
My factory interface:
public interface IFactory<T>
{
T Create();
}
My presenter and view:
public sealed class Presenter
{
private readonly View view;
public Presenter(View view)
{
this.view = view;
}
}
public sealed class View
{
public View()
{
}
}
First, I'll show what works perfectly, that Ninject resolves as expected. This will not include the abstract base class I mentioned at the beginning. After this, I'll add the slight modifications with the abstract base class that will make Ninject throw when trying to resolve the dependencies.
We see above that the presenter depends on the view, so the presenter factory will depend on the view factory:
public sealed class GoodPresenterFactory : IFactory<Presenter>
{
private readonly IFactory<View> viewFactory;
public GoodPresenterFactory(IFactory<View> viewFactory)
{
this.viewFactory = viewFactory;
}
public Presenter Create()
{
return new Presenter(this.viewFactory.Create());
}
}
public sealed class ViewFactory : IFactory<View>
{
public ViewFactory()
{
}
public View Create()
{
return new View();
}
}
Wiring this up with Ninject:
Bind<IFactory<Presenter>>().To<GoodPresenterFactory>();
Bind<IFactory<View>>().To<ViewFactory>();
And then resolving the presenter factory:
var presenterFactory = container.Get<IFactory<Presenter>>();
Everything up until now works perfectly. The dependency on the view factory inside the presenter factory is resolved as expected.
Now, I have a million classes that looks like GoodPresenterFactory above and I therefore wanted a small base class to handle some trivial common stuff, like the dependency on the view factory in the presenter factory:
public abstract class FactoryBase<T, U> : IFactory<T>
{
protected readonly U dependency;
protected FactoryBase(U dependency)
{
this.dependency = dependency;
}
public abstract T Create();
}
Then the presenter factory will change and something in that change will make Ninject fail resolving:
public sealed class BadPresenterFactory : FactoryBase<Presenter, IFactory<View>>
{
public BadPresenterFactory(IFactory<View> viewFactory)
: base(viewFactory)
{
}
public override Presenter Create()
{
return new Presenter(this.dependency.Create());
}
}
And changing the Ninject wiring accordingly:
Bind<IFactory<Presenter>>().To<BadPresenterFactory>();
Bind<IFactory<View>>().To<ViewFactory>();
Those changes will make Ninject throw an ArgumentNullException when doing
var presenterFactory = container.Get<IFactory<Presenter>>();
Call stack from the exception:
at System.Reflection.RuntimeMethodInfo.GetParentDefinition()
at System.Reflection.CustomAttribute.IsDefined(MemberInfo member, Type caType, Boolean inherit)
at System.Reflection.RuntimeMethodInfo.IsDefined(Type attributeType, Boolean inherit)
at System.Attribute.IsDefined(MemberInfo element, Type attributeType, Boolean inherit)
at System.Attribute.IsDefined(MemberInfo element, Type attributeType)
at Ninject.Infrastructure.Language.ExtensionsForMemberInfo.HasAttribute(MemberInfo member, Type type)
at Ninject.Selection.Heuristics.StandardInjectionHeuristic.ShouldInject(MemberInfo member)
at Ninject.Selection.Selector.<>c__DisplayClassa.<SelectMethodsForInjection>b__9(IInjectionHeuristic h)
at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate)
at Ninject.Selection.Selector.<SelectMethodsForInjection>b__8(MethodInfo m)
at System.Linq.Enumerable.<WhereIterator>d__0`1.MoveNext()
at Ninject.Planning.Strategies.MethodReflectionStrategy.Execute(IPlan plan)
at Ninject.Planning.Planner.<>c__DisplayClass2.<GetPlan>b__0(IPlanningStrategy s)
at Ninject.Infrastructure.Language.ExtensionsForIEnumerableOfT.Map[T](IEnumerable`1 series, Action`1 action)
at Ninject.Planning.Planner.GetPlan(Type type)
at Ninject.Activation.Providers.StandardProvider.Create(IContext context)
at Ninject.Activation.Context.Resolve()
at Ninject.KernelBase.<Resolve>b__4(IContext context)
at System.Linq.Enumerable.<SelectIterator>d__d`2.MoveNext()
at System.Linq.Enumerable.<CastIterator>d__b0`1.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters)
at NinjectTest.Program.Main()
If I modify FactoryBase
so that it has no dependency, it's just a naked base class, then Ninject also fails.
public abstract class NakedFactoryBase<T> : IFactory<T>
{
protected NakedFactoryBase()
{
}
public abstract T Create();
}
public sealed class PointlessPresenterFactory : NakedFactoryBase<Presenter>
{
private readonly IFactory<View> viewFactory;
public PointlessPresenterFactory(IFactory<View> viewFactory)
{
this.viewFactory = viewFactory;
}
public override Presenter Create()
{
return new Presenter(this.viewFactory.Create());
}
}
As you can see, that failing PointlessPresenterFactory
is identical to the succeeding GoodPresenterFactory
, apart from the direct IFactory<Presenter>
implementation in GoodPresenterFactory
, as opposed to the completeley naked base class used in PointlessPresenterFactory
.
Any idea why Ninject fails to resolve when the factory base class is used?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
此问题已在版本 2.3.0.46 中修复,并将成为下一版本 (2.4) 的一部分。
注意:因为 CF 似乎不允许检测方法是否是通用的,所以 Inject 属性不能再在基本方法上定义。它必须在重载方法上定义。
This issue has been fixed in build 2.3.0.46 and will be part of the next release (2.4).
NOTE: because CF seems not to allow to detect is a method is generic or not the Inject attribute can not be defined on base methods anymore. It has to be defined on the overload method.