1

I want to let Ninject resolve an instance of T based on a specific enum input value.

I have read about Ninject's factory extension, but I couldn't find any example having the factory resolve a specific class based on an enum.

Each class derives from a base class and that derived class has several, different interfaces that Ninject also has to resolve.

For example this is how the interface should look like:

public interface IProcessFactory
{
    T Create<T>(ProcessIndex processIndex) where T : BaseProcess;
}

How can this be achieved ?

Patrick Peters
  • 9,456
  • 7
  • 57
  • 106

2 Answers2

3

This is not supported out of the box. You can customize it by writing your own implementation of IInstanceProvider(also see ninject Wiki entry. Then configure it for your specific factory:

kernel.Bind<IFooFactory>()
      .ToFactory(() => new MyCustomInstanceProvider());

Or alternatively, if you want to change the behavior of all .ToFactory() bindings: Rebind IInstanceProvider after loading Ninject.Extensions.Factory:

kernel.Rebind<IInstanceProvider>().To<MyCustomInstanceProvider>();

However, if it's not something you need often i would consider manually writing a factory implementation @ composition root.

Anyway, in both cases you'll need to know how to create a conditional binding. Ninject calls it Contextual Binding. One method is to use Binding-Metadata:

const string EnumKey = "EnumKey";

Bind<IFoo>().To<AFoo>()
            .WithMetadata(EnumKey, MyEnum.A);

IResolutionRoot.Get<IFoo>(x => x.Get<MyEnum>(EnumKey) == MyEnum.A);

Another way would be to create a custom IParameter and use in a conditional binding:

Bind<IFoo>().To<AFoo>()
            .When(x => x.Parameters.OfType<MyParameter>().Single().Value == A);
BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
  • 2
    Better to use kernel.Bind().ToFactory(() => new MyCustomInstanceProvider()); This let's you define which instance provider should be used for a specific factory binding. – treze May 06 '15 at 09:53
  • @treze thanks this is a good point i'll include it in my post. What we did however is add some more features to the InstanceProvider which we used in a lot of places. In that case i think it is actually better to provide it "globally" instead of specific per binding. – BatteryBackupUnit May 06 '15 at 12:04
  • If you want it to be your default instance provider it makes of course sense – treze May 06 '15 at 12:25
  • No of the supplied solution work. I have a Ninject Module where all bindings are done. I currently have a severe performance issue having a custom ProcessFactory : IProcessFactory, having all the processes injected where the Create method returns the correct injected instance by the enum value. That is not what I want, I want Ninject resolve the correct instance on demand... – Patrick Peters Jun 09 '15 at 13:13
  • @PatrickPeters can you please explain where the provided solutions fail. The correct instance is resolved and instanciated on demand. The solution doesn't require all implementations of `T` created (injected into the factory) just to create the one you want (specify by the enum). – BatteryBackupUnit Jun 09 '15 at 14:33
0

There are several options available to implement AbstractFactory using DI (Ninject).

After analyzing the options, I came up with the solution provided by Mark Seemann, see http://blog.ploeh.dk/2012/03/15/ImplementinganAbstractFactory/

The Container Based Factory solution is the one I chose, because:

  • Performance: on demand DI resolve on request, no instances loaded in the constructor
  • Easy for refactor: when we want to replace the current DI framework (Ninject) to a much better performer with (almost or even better) featureset, the only place to change are the calls inside the factory and not in the NinjectModules/Composition Root.

See also at SO: Simple Injector:Factory classes that need to create classes with dependencies

Community
  • 1
  • 1
Patrick Peters
  • 9,456
  • 7
  • 57
  • 106