It's entirely possible my approach is incorrect but I'd like to outline the actual requirements first before my attempt at a resolution. My approach is based on the details provided here
Task: in a wizard oriented stucture, get a BaseWizardStepNavigator
object based on the current step. E.g., if I'm on step WizardStep.Step1
, return instance of Step1Navigator
. The instance of Step1Navigator
should have any injected assemblies supplied in its constructor such that if I have;
public class Step1Navigator : BaseWizardStepNavigator
{
private readonly ISomeReader _reader;
public Step1Navigator(ISomeReader reader)
: base(WizardSteps.Step1)
{
_reader = reader;
}
}
...that argument reader
is populated with the appropriate implementation.
My idea is that I'd have a manager object that ninject can instantiate by passing in all implementations of the base class (with appropriate IoC injections) such that;
public class NavigatorManager
{
private readonly List<BaseWizardStepNavigator> _navigators;
public class NavigatorManager(IEnumerable<BaseWizardStepNavigator> navigators)
{
_navigators = new List<BaseWizardStepNavigator>(navigators);
}
public BaseWizardStepNavigator Get(WizardStep step)
{
return _navigators.FirstOrDefault(n => n.Step == step);
}
}
There will eventually be 10's of wizard steps with appropriate navigators to determine what the next step is but they'll need to hit the DB occasionally to do that.
My current attempt and performing the binding in a NinjectModule
where I'm using Ninject and Ninject.Conventions is;
Module (load method);
Kernel.Bind(s => s.FromAssemblyContaining(typeof(BaseWizardStepNavigator))
.SelectAllClasses()
.WhichAreNotGeneric()
.InheritedFrom<BaseWizardStepNavigator>()
.BindWith<NavigatorBinding>());
var test = Kernel.GetAll<BaseWizardStepNavigator>();
Then other classes for the bindings and provider;
public class NavigatorBinding : IBindingGenerator
{
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
if (type.IsInterface || type.IsAbstract)
{
yield break;
}
yield return bindingRoot.Bind(typeof(BaseWizardStepNavigator)).ToProvider<NavigatorProvider>();
}
}
public class NavigatorProvider : IProvider<BaseWizardStepNavigator>
{
public object Create(IContext context)
{
return null;
}
public Type Type { get { throw new NotImplementedException(); } }
}
Now, while the call to kernel.GetAll<BaseWizardStepNavigator>()
does call the Provider
implementation methods, I'm a bit lost as to how to actually get it to spit back the objects. The documentation is obscure and I'm not entirely certain I'm even on the correct path. Help?
I managed to get an implementation working fairly simply in the end. There was no need for IBindingGenerator
or IProvider
implementations.
Code for Step1Navigator
and NavigatorManager
remains the same.
NinjectModule
binding code changes to;
// set the navigator bindings
Kernel.Bind(s => s.FromAssemblyContaining(typeof(BaseWizardStepNavigator))
.SelectAllClasses()
.WhichAreNotGeneric()
.InheritedFrom<BaseWizardStepNavigator>()
.BindAllBaseClasses()
.Configure(c => c.InRequestScope())
);
// pass in all children of BaseWizardStepNavigator to the manager instance
Bind<NavigatorManager>().ToSelf()
.InRequestScope()
.WithConstructorArgument(typeof(IEnumerable<BaseWizardStepNavigator>),
n => n.Kernel.GetAll<BaseWizardStepNavigator>());
The .InRequestScope()
is specific to web applications. Change as appropriate if you're using this in your own code to .InSingletonScope()
, etc.