在Ninject 2中,我如何拥有两个具有不同设置共享绑定的内核?

时间:2010-10-04 16:41:42

标签: ninject ninject-2

我的应用程序有一个Ninject 2内核,其中包含所有绑定。应用程序的一部分需要在内核上具有与应用程序其余部分不同的设置,但需要相同的绑定(该部分适用于NHibernate,需要InjectNonPublic = trueInjectAttribute设置)。如何使内核与当前内核共享绑定但具有不同的设置?

我相信在其他IOC容器中,这将通过“嵌套容器”实现,但是我没有看到Ninject中嵌套容器的任何支持?

2 个答案:

答案 0 :(得分:1)

您是否尝试过Ninject.Extensions.ChildKernel扩展程序?

答案 1 :(得分:1)

我最终解决了我的问题。正如我所说,我真正在做的是尝试自定义注入以供NHibernate使用。为了解决这个问题,我最终使用Ninject的内部IOC来替换其行为的某些策略。

在内部,Ninject使用ISelector的一个实例来确定它应该考虑调用哪些构造函数。我通过修饰标准Selector重新实现了这个类(我无法将标准子类化并覆盖,因为没有一个方法是虚拟的)。然后,我可以有条件地更改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>();
    }
}