我的应用程序有一个 Ninject 2 内核,其中包含所有绑定。应用程序的一部分需要在内核上具有与应用程序的其余部分不同的设置,但需要相同的绑定(该部分用于 NHibernate 和需要InjectNonPublic = true
和InjectAttribute
集合)。如何制作一个与当前内核共享绑定但具有不同设置的内核?
我相信在其他 IOC 容器中,这将通过“嵌套容器”来实现,但是我看不到 Ninject 中对嵌套容器的任何支持?
Have you tried the Ninject.Extensions.ChildKernel extension?
我最终解决了我的问题。正如我所说,我真正在做的是尝试自定义注入以供 NHibernate 使用。为了解决这个问题,我最终使用 Ninject 的内部 IOC 来替换其行为的某些策略。
在内部,Ninject 使用 ISelector 的一个实例来确定它应该考虑调用哪些构造函数。我通过装饰标准选择器重新实现了这个类(我不能继承标准并覆盖,因为没有一个方法是虚拟的)。然后我可以有条件地改变SelectConstructorsForInjection()
方法的行为。
public class HydratingSelector : DisposableObject, ISelector
{
private readonly ISelector standardSelector;
private const BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
public HydratingSelector(IConstructorScorer constructorScorer,
IEnumerable<IInjectionHeuristic> injectionHeuristics)
{
standardSelector = new Selector(constructorScorer, injectionHeuristics);
}
public IEnumerable<ConstructorInfo> SelectConstructorsForInjection(Type type)
{
if(Settings.GetHydratedTypeSelector()(type))
{
var constructors = type.GetConstructors(Flags);
return constructors.Length != 0 ? constructors : null;
}
return standardSelector.SelectConstructorsForInjection(type);
}
// Omitted implementations of other methods that just forward to standardSelector
}
上面的内容基本上是InjectNonPublic = true
为水合类型做的。但我仍然需要InjectAttribute
为这些类型设置等效项。我这样做是通过替换HydratingConstructorScorer
类似的:
public class HydratingConstructorScorer: DisposableObject, IConstructorScorer
{
private readonly IConstructorScorer standardScorer = new StandardConstructorScorer();
public int Score(IContext context, ConstructorInjectionDirective directive)
{
if(Settings.GetHydratedTypeSelector()(directive.Constructor.DeclaringType)
&& directive.Constructor.HasAttribute(Settings.GetHydrateAttribute()))
return int.MaxValue;
return standardScorer.Score(context, directive);
}
// Omitted implementations of other methods that just forward to standardSelector
}
然后我通过创建一个特殊的内核来整合这些新策略,如下所示:
public class HydratingKernel : StandardKernel
{
public HydratingKernel(params INinjectModule[] modules)
: base(modules)
{
}
public HydratingKernel(INinjectSettings settings, params INinjectModule[] modules)
: base(settings, modules)
{
}
protected override void AddComponents()
{
base.AddComponents();
SetupComponentsForHydratingKernel(Components);
}
internal static void SetupComponentsForHydratingKernel(IComponentContainer components)
{
components.RemoveAll<ISelector>();
components.Add<ISelector, HydratingSelector>();
components.RemoveAll<IConstructorScorer>();
components.Add<IConstructorScorer, HydratingConstructorScorer>();
}
}