I'm using Ninject 3.0.1.10 and ninject.extensions.factory 3.0.1.0 from NuGet - in the 'real' scenario I'll be using ninject.extensions.conventions as well (rather than manually binding IFoo), but I wanted to keep that out of this to try and simplify the question.
I have an IFoo interface and multiple implementations of it, with each under a child namespace and subfolder called Gen1 and Gen2. I have an IFooFactory interface where the intent is that it returns an IFoo based on a specified parameter (string, enum, whatever).
I use an enum in this example simply to try and make it more clear - I had made a string version at first, but felt like the objections around passing a more arbitrary parameter like a string would just confuse the issue.
public enum ImplementationGeneration
{
Gen1,
Gen2,
Gen3,
}
public interface IFoo
{
void DoStuff();
}
public interface IFooFactory
{
IFoo CreateFoo(ImplementationGeneration implementationGeneration);
}
namespace SomeRootNamespace.Gen1
{
public class Foo : IFoo
{
public void DoStuff()
{
Console.WriteLine("Doing Type1 stuff");
}
}
}
namespace SomeRootNamespace.Gen2
{
public class Foo : IFoo
{
public void DoStuff()
{
Console.WriteLine("Doing Type2 stuff");
}
}
}
Now, I understand that having the consumer 'choose' the implementation like this is a form of coupling that would ideally not exist, but IMHO it's the same level of coupling as named bindings, which Ninject already supports. I wanted to avoid adding the attributes to the implementations, and having GetGen1 / GetGen2 / etc methods in the factory interface is a painful fit for this, since I'd end up violating OCP via a switch somewhere to map input to the method to call (or manually using reflection)
The complete/working code I have now that I'd rather avoid if possible is here: https://gist.github.com/4549677
It uses 2 approaches:
- a manual factory implementation that violates OCP with a switch on the enum passed
- using the factory extension with an instance of IInstanceProvider (subclasses StandardInstanceProvider to override GetInstance).
The second approach seems like it might be 'close' to the 'right way' to get this working, but 1) it keeps around a reference to the kernel in order to do its work, which is probably a bad idea and 2) since I couldn't find the concrete type in the bindings for IFoo when searching on all the IFoo bindings during the call, it currently does a GetAll, so it instantiates N-1 more instances than it needs to for this scenario.