1

According to this article (first paragraph), it is bad practice to inject your IKernel into wherever you need it.

Instead it is proposed to introduce a factory interface that is automatically implementend by Ninject (doing internally the same resolution).

This is an actual code snipped I am working on:

Former implementation:

public class CommandServer
{
    [Inject]
    public IKernel Kernel { get; set; }

    ....

   public TResponse ExecuteCommand<TRequest, TResponse>(TRequest request)
        where TResponse : ResponseBase, new()
    {
        ...
        var command = Kernel.Get<ICommand<TRequest, TResponse>>();
        ...
    }
}

Using a factory:

public class CommandServer
{
    [Inject]
    public ICommandFactory CommandFactory { get; set; }

    ....

   public TResponse ExecuteCommand<TRequest, TResponse>(TRequest request)
        where TResponse : ResponseBase, new()
    {
        ...
        var command = CommandFactory.CreateCommand<TRequest, TResponse>();
        ...
    }
}

// at binding time:
public interface ICommandFactory
{
    ICommand<TRequest, TResponse> CreateCommand<TRequest, TResponse>();
}

Bind<ICommandFactory>().ToFactory();

I am not saying I don't like it (it looks nice and clean) - just not exactly sure why the former is particularly bad and the latter is so much better?

Wolfgang
  • 2,188
  • 1
  • 25
  • 24

1 Answers1

3

Generally you should not be using the Service Locator pattern. Why you ask? Please see Mark Seeman(comments, too!) and this SO question. Using the IKernel (or somewhat better: only the IResolutionRoot part of it) smells like Service Locator.

Now Mark would suggest that you should apply the Abstract Factory Pattern instead - and he also mentions the Dynamic proxy approach.

I personally think that using ninject auto-generated factories (= dynamic proxy approach) instead is worth the trade off. You should not use a factory like:

public interface IServiceLocator
{
    T Create<T>();
}

because well.. it's service locator ;-)

However, using something like

public interface IResponseHandleFactory
{
    IResponseHandle Create(int responseId);
}

is perfectly fine.

Of course you can also do this by using the IResolutionRoot directly - instead of the factory. The code would look like:

IResolutionRoot.Get<IResponseHandle>(
    new ConstructorArgument("responseId", theResponseIdValue);

Reasons not to use IResolutionRoot directly

  • A lot of the IResolutionRoot "methods" are in fact extension methods. That complicates unit-testing a lot (it's basically not a sensible choice if you want to unit test it, at all).
  • slight worse decoupling from container (=> ease of changing DI containers) than when using a factory interface. The auto-generated factory feature you can also implement as an add on to other containers - if they don't have it already (i've done so personally for Unity and AutoFac). However it requires some know-how about dynamic proxies.

Alternative to factory interfaces: Using Func<> factories. The above example could also be replaced by Func<int, IResponseHandle>(). Quite a lot DI containers support this out of the box / with standard plugins (ninject needs the Factory extension). So you'd be decoupled from the container even more. Disadvantage: harder to unit test and not clearly named parameters.

Community
  • 1
  • 1
BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
  • What is the best way you recommend then? When using `IResponseHandle Create(int responseId);` there needs to be an instantiation at some point also? – Ozkan Jun 26 '18 at 08:41
  • @Ozkan The implementation for this can be done "automagically" in a tried & tested way: [Ninject.Extensions.Factory](https://github.com/ninject/Ninject.Extensions.Factory). An alternative is to write the implementation of `IResponseHandleFactory` manually (note: do make it part of the composition root! - also see Mark Seeman's blgo posts on the topic) – BatteryBackupUnit Jun 26 '18 at 09:52