Search code examples
.netdependency-injectionninjectninject-extensions

Creating a custom Ninject provider for multiple types implementing the same interface


I have an interface, let's say it's IDrawingTool. I have multiple classes implementing this interface, let's say PencilTool, PenTool, YellowMarkerTool, etc. I am usually binding more than one of these classes in Ninject, and I always access IDrawingTool instances by calling kernel.GetAll<IDrawingTool>. So far so good.

Now, I want to create a new IDrawingTool implementation, ConfigurableBrushTool, that can be configured in many different ways (let's say brush color, brush thickness, etc).

I'd like to be able to have some sort of a "factory"/"provider" that will let me inject multiple IDrawingTools (i.e. multiple ConfigurableBrushTools with different configurations). That is, I want to be able to do something like the following:

kernel.Bind<IDrawingTool>.To<PencilTool>();
kernel.Bind<IDrawingTool>.To<PenTool>();
kernel.Bind<IDrawingTool>.ToTypeProvider<ConfigurableBrushToolProvider>();
//...where ConfigurableBrushToolProvider reads e.g. 50 different
//brush configurations (color, thickness, etc) from the database/file/network/whatever
//and binds 50 different ConfigurableBrushTool instances.
//Of course, .ToTypeProvider() doesn't really exist :)

//Later on:
var tools = kernel.GetAll<IDrawingTool>(); //Should return pencil, pen and all 50 brushes

I haven't been able to find a way to make this happen with Ninject.

I've looked at implementing IProvider / Provider<T>, but it only lets me return one instance of the type I'm providing, there's no way to return multiple instances in bulk.

I've also looked at IMissingBindingResolver, and it's very close to what I need: If I create such a resolver for IDrawingTool, I am able to return multiple bindings for multiple ConfigurableBrushTools. However, it only works if the binding is missing (i.e. if there are no other IDrawingTool bindings). As soon as I add my PenTool and PencilTool bindings, the binding for IDrawingTool is no longer "missing" and so my custom resolver is no longer invoked.

Is there a way to make my scenario happen? I.e. how can I bind an interface to both (1) specific types implementing the interface, and (2) a "factory"/"provider" of many instances that implement the interface, such that GetAll will return all of the bindings, both (1) and (2)?


Solution

  • First of all, looks like there is no OOTB feature to make multiple bindings from one binding set in module (at least I didn't find any obvious one).
    Secondly, I managed to create some PoC with custom Kernel that allows you to create one-to-many binding with one .Bind.To call. Is is based on overriding IKernel.GetBindings(..) method and finding all bindings with special parameter. Then we can remove them, replacing by amount of custom generated.
    However, I think such logic will have huge impact on performance if not carefully optimized (which is not done for PoC anyway) and should make it to production only if it is really needed. This PoC you can find here.
    As for me, it is much better approach to refactor existing code to inject a factory returning array created on-the-fly.