Ninject to return implementation based on enumerator

54 Views Asked by At

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?

1

There are 1 best solutions below

0
On BEST ANSWER

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.