使用 Ninject.Factory 比仅仅注入 IKernel 有什么好处?

What is the benefit of using a Ninject.Factory over just injecting the IKernel?

根据 this article(第一段),将 IKernel 注入任何需要它的地方是不好的做法。

相反,建议引入一个由 Ninject 自动实现的工厂接口(在内部进行相同的解析)。

这是我正在处理的实际代码片段:

以前的实现:

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>>();
        ...
    }
}

使用工厂:

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();

我并不是说我不喜欢它(它看起来又好又干净)- 只是不确定为什么前者特别糟糕而后者好得多?

通常您不应该使用服务定位器模式。你为什么问?请参阅 Mark Seeman(comments, too!) and this SO question。使用 IKernel(或更好:仅 IResolutionRoot 部分)闻起来 服务定位器。

现在 Mark 建议您改为应用 Abstract Factory Pattern - 他还提到了动态代理方法。

我个人认为使用 ninject 自动生成的工厂(= 动态代理方法)是值得的。 你应该使用像这样的工厂:

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

因为……它是服务定位器 ;-)

但是,使用

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

完全没问题。

当然,您也可以直接使用 IResolutionRoot 来完成此操作 - 而不是工厂。代码如下所示:

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

不直接使用IResolutionRoot的原因

  • 很多 IResolutionRoot "methods" 实际上是扩展方法。这使单元测试变得复杂很多(如果你想对它进行单元测试,这基本上不是一个明智的选择)。
  • 轻微 与容器的解耦(=> 易于更改 DI 容器)比使用工厂接口时更差。您还可以将自动生成的工厂功能实现为对其他容器的附加——如果它们还没有(我已经为 Unity 和 AutoFac 亲自这样做了)。然而,它需要一些关于动态代理的知识。

工厂接口的替代方法:使用 Func<> 工厂。上面的例子也可以用Func<int, IResponseHandle>()代替。很多 DI 容器开箱即用/使用标准插件支持这一点(ninject 需要 Factory 扩展)。所以你会更加脱离容器。缺点:难以进行单元测试且参数命名不明确。