In Ninject 2, how do I have two Kernels with different settings share bindings?

StackOverflow https://stackoverflow.com/questions/8886459

  •  16-04-2021
  •  | 
  •  

Pregunta

I have a single Ninject 2 Kernel for my application which contains all bindings. One section of the application needs to have different settings on the Kernel than the rest of the application, but needs the same bindings (that portion is for NHibernate and needs InjectNonPublic = true and the InjectAttribute set). How can a make a Kernel that shares bindings with the current kernel but has different settings?

I believe that in other IOC containers this is would be achieved with a "nested container", however I don't see any support for nested containers in Ninject?

¿Fue útil?

Solución 2

I eventually solved my problem. As I said, what I was really doing was trying to customize injection for use by NHibernate. In order to solve this I ended up using Ninject's internal IOC to replace certain strategies for its behavior.

Internally, Ninject uses an instance of ISelector to determine which constructors it should consider invoking. I reimplemented this class by decorating the standard Selector (I couldn't subclass the standard and override because none of the methods are virtual). I could then conditionally change the SelectConstructorsForInjection() method's behavior.

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
}

The above took care of essentially doing InjectNonPublic = true for the hydrated types. But I still needed to do the equivalent of setting the InjectAttribute for those types. This I did by replacing the HydratingConstructorScorer like so:

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
}

I then incorporated these new strategies by creating a special Kernel like so:

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>();
    }
}

Otros consejos

Have you tried the Ninject.Extensions.ChildKernel extension?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top