0
votes

So far I've managed to easily implement contextual binding using Unity and Castle Windsor. Meaning, given multiple registered implementations of an interface, I can register the client component that uses those as a dependency to pick a specific one. However with Ninject, there doesn't seem to be the same interface for performing this and the example I found on:

https://github.com/ninject/Ninject/wiki/Contextual-Binding#simple-constrained-resolution-named-bindings

Unity example:

Given interface, IFoo and implementations Foo1, Foo2, Foo3.

container.RegisterType<IFoo , Foo1>("Foo1", new TransientLifetimeManager());
container.RegisterType<IFoo , Foo2>("Foo2", new TransientLifetimeManager());
container.RegisterType<IFoo , Foo3>("Foo3", new TransientLifetimeManager());
container.RegisterType<FooClient, FooClient>("FooClient1", new InjectionConstructor(new ResolvedParameter<IFoo>("Foo2")));
var fooClient = container.Resolve<FooClient>("FooClient1");

Castle Windsor example:

internal class FooInstaller: IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Component.For<IFoo>().Named("Foo1").ImplementedBy<Foo1>().LifeStyle.Transient);
        container.Register(Component.For<IFoo>().Named("Foo2").ImplementedBy<Foo2>().LifeStyle.Transient);
        container.Register(Component.For<IFoo>().Named("Foo3").ImplementedBy<Foo3>().LifeStyle.Transient);
        container.Register(Component.For<FooClient>().LifeStyle.Singleton.DependsOn(ServiceOverride.ForKey<IFoo>().Eq("Foo2")));
    }
}
var fooClient = container.Resolve<FooClient>();

Ninject(using binding metadata):

class FooClient{
    public FooClient(Named["Foo2"] IFoo fooDependency)
    {
    }
    ....
}

is even admitted in the documentation to be an anti-pattern by directly using service locator to ask for a specific implementation instead of relying on auto-resolution.

The Unity & Castle Windsor worked exactly as expected, fooClient used the Foo2 implementation of IFoo. It was automatically resolved.

Regarding the Ninject approach, this worked but that means it's hard-baked into the implementation of the higher-level component and I've violated dependency inversion principle. The nice part about the Unity/Windsor approach is the composition root(IOC setup) is the isolation point.

Is there something on the Ninject API that I missed that would allow me to preserve the DIP?

1

1 Answers

0
votes

Conditional Binding

You can use:

Bind<FooClient>().ToSelf();
Bind<IFoo>().To<Foo2>().WhenInjectedInto<FooClient>();

Binding Paramter

Bind<Foo2>().ToSelf();

Bind<FooClient>().ToSelf()
    .WithConstructorArgument(typeof(IFoo)), ctx => ctx.Kernel.Get<Foo2>());

Note: of course you could also resolve Kernel.Get<IFoo>("foo2"). Note: typed "from memory", might not compile because syntax might be slightly different.