As in the link provided, you are intending to look up a value by Key
.
What you are searching for is called named binding. Bindings are Identified by a Name. How ever the Problem here is, that you need to use the common injection strategies of ninject.
Bind<IDeviceState>().To<OnlineState>().Named("Online");
Bind<IDeviceState>().To<OfflineState>().Named("Offline");
public Modem([Named("Online")] IDeviceState state){
_state = state;
}
This way, you can choose which implementation of IDeviceState
will be injected by the NamedAttribute
, which is added right before the injection parameter. But this doesn't fully work out for you, because you want to create IDeviceState
s on the fly. Thats why you need to use the Ninject.Extensions.Factory.
This will allow you to Inject a IDeviceStateFactory
.
public interface IDeviceStateFactory
{
IDeviceState GetOnline();
IDeviceState GetOffline();
}
Those Factorys underlie a very specific syntax, where Get
-Methods will resolve a Named Binding as described above. So GetOnline()
will match the Bind<IDeviceState>().To<OnlineState>().Named("Online")
binding and return an instance of OnlineState
.
Just tell the IoCContainer
that IDeviceStateFactory
is a factory. An implementation of that interface will then automatically be provided by Ninject's Proxy system.
Bind<IDeviceStateFactory>().ToFactory();
So finally this is how you could imlpement your class Modem
using this technique
public class Modem : IHardwareDevice
{
IDeviceStateFactory _stateFactory;
IDeviceState _currentState;
public Modem(IDeviceStateFactory stateFactory)
{
_stateFactory = stateFactory;
SwitchOn();
}
void SwitchOn()
{
_currentState = _stateFactory.GetOnline();
}
}
Using a real Key
However if you really want to use your DeviceState
-Enumeration like before, you could use the approach described here.
The Binding Would look pretty much like this:
const string EnumKey = "EnumKey";
Bind<IDeviceState>().To<OnlineState>()
.WithMetadata(EnumKey, DeviceState.Online);
Use this Method to resolve using DeviceState.Online
Key
IResolutionRoot.Get<IDeviceState>(x => x.Get<DeviceState>(EnumKey) == DeviceState.Online);
But in my opinion, there is no reason to do such complicated thing. I recommend you to just forget about the whole IIndex<TKey, TSource>
mechanics from Autofac and do it the ninject way.
Your Edit: preserving IIndex (Solution involving Factory Extension)
Well, as IIndex
and no such thing do exist in Ninject, you could create your own implementation of it, Inheriting from IDictionary
, or just define it much alike it and only defining it as follows:
public interface IIndex<TKey, TValue>
{
bool TryGetValue(TKey key, out TValue value);
TValue this[TKey key] { get; set; }
}
The Factory from above then gets ICommandFactory
public interface ICommandFactory
{
ICommand GetAdd();
ICommand GetSubtract();
}
then you only need to create an implementation of IIndex
using the ICommandFactory
to resolve the actual command instances.
public class CommandIndex : IIndex<string, ICommand>
{
private readonly ICommandFactory _factory;
public CommandIndex(ICommandFactory factory)
{
_factory = factory;
}
public bool TryGetValue(string key, out ICommand value)
{
switch (key)
{
case "Add":
value = _factory.GetAdd();
break;
case "Subtract":
value = _factory.GetSubtract();
break;
default:
value = null;
break;
}
return value != null;
}
public ICommand this[string key]
{
get
{
ICommand value;
TryGetValue(key, out value);
return value;
}
set { throw new NotSupportedException();}
}
}
But I really need to ask you: Is this whole thing necessary? Man just use the factory. It really gets much less complicated.
Another attempt of preserving IIndex without Factory Extension
Another Attempt with propably less coding is directly using the IKernel
which is propably a slightly bad practice. However you can then resolve named bindings like this:
public class NamedBindingIndex<TValue> : IIndex<string, TValue>
{
private readonly IKernel _kernel;
public NamedBindingIndex(IKernel kernel)
{
_kernel = kernel;
}
public bool TryGetValue(string key, out TValue value)
{
value = _kernel.Get<TValue>(key); // eventually catch an Exception here
return value != null;
}
public TValue this[string key]
{
get
{
TValue value;
TryGetValue(key, out value);
return value;
}
set { throw new NotSupportedException(); }
}
}
Just bind it like this
Bind<IIndex<string, ICommand>>().To<NamedBindingIndex<ICommand>>();
And then your Solution should be working pretty much as same. Another plus is, that this very last attempt doesn't need no Ninject.Extensions.Factory.