依赖注入可选参数

Dis*_*ile 4 c# asp.net-mvc dependency-injection optional-parameters

在使用带有Constructor注入的依赖注入框架时,使用可选参数被认为是不好的做法吗?

例:

public class ProductsController
{
    public ProductsController(IProductService productService = null, IBackOrderService = null)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

我已将这两个参数指定为可选参数,但我的DI框架将始终注入两个依赖项.如果我向我的控制器添加一个需要新依赖项的新操作,那么使新的依赖项可选是不是很糟糕?即使现有的测试不需要新的依赖,我也可能打破几十个单元测试.

编辑
人们似乎对我的问题感到困惑.我永远不会在我的Web应用程序中手动构建ProductsController.这由控制器工厂(自动注入依赖项)处理.

我不喜欢的是进行这样的单元测试:

[Test]
public void Test1()
{
    var controller = new ProductsController(new MockProductService(), new MockBackOrderService());
}
Run Code Online (Sandbox Code Playgroud)

现在我决定向我的控制器添加一个新的动作方法.此新操作需要新的依赖项,但现有操作都不需要.现在我必须返回并修改100个不同的单元测试,因为我添加了一个新参数.我可以通过使参数可选来避免这种情况,但我想知道这是不是一个坏主意.我的直觉不是,因为它唯一影响的是单元测试.

Pio*_*ula 9

我完全同意所有案例的公认答案,即定义依赖项意味着实现将无法工作。

但是,如果您的某些内容不一定需要依赖项,但您希望能够在已加载该依赖项的情况下进行配置,该怎么办。好的...?这听起来有点奇怪,但它是一个有效的元编程用例 - 你认为工厂模式可能会有所帮助......但即使是工厂也可能需要一些依赖,没有或全部依赖,所以工厂不能解决这个问题。

特征标志的现实世界问题。如果某个功能被关闭,您就不需要该依赖项……或者甚至无法创建它,因为没有具体的实现。所以它关闭了。它编译,一切正常。但是后来有人打开了这个功能,突然间我们需要这种依赖。

我找到了一种方法来做到这一点 - 最好的部分是我只是通过学习另一种不太知名的依赖注入技术(我正在使用 Microsoft.Extensions.DependencyInjection)才意识到如何做到这一点

为单个接口注入多个具体实现

services.AddTransient<IWarehouseRepo, ActionRepository>();
services.AddTransient<IWarehouseRepo, EventRepository>();
services.AddTransient<IWarehouseRepo, AuditRepository>();
services.AddTransient<IWarehouseRepo, ProRepository>();
services.AddTransient<IWarehouseRepo, SuperWarehouseRepository>();
services.AddTransient<IWarehouseRepo, InferiorWarehouseRepository>();
services.AddTransient<IWarehouseRepo, MonthlyStatisticsRepository>();
Run Code Online (Sandbox Code Playgroud)

我最近才知道这是完全有效的,但要使其正常工作,您的构造函数需要看起来像这样..

public WarehouseHandler(IEnumerable<IWarehouseRepo> repos)
Run Code Online (Sandbox Code Playgroud)

所以这太酷了!我可以根据任何标准选择我需要的存储库。但这对可选依赖项有何帮助?

因为这种类型的构造函数会给你 0 个或多个依赖项。因此,如果您不添加任何依赖项,则可以使用条件语句在构造函数中进行分支

  if (repos.Count() == 0)
    return;
Run Code Online (Sandbox Code Playgroud)

这是空引用安全的,不需要默认值,易于调试,易于阅读和易于实现。

您现在也可以将此技术用作功能切换机制!

  • 赞成 - 这可能不是OP所要求的,但在不一定与单元测试相关的情况下肯定有所帮助。 (2认同)

Tom*_*uλa 6

我不认为这是个好主意.构造函数注入意味着需要依赖项.如果其中一个参数为null,您甚至应该添加引发的保护线.

我认为问题在于您的单元测试.例如,我只有一个地方创建了控制器,并且模拟了支持对象(controllerContext,HttpContext,Request,Response等).然后,如果我在构造函数中添加一个新参数,我必须在单元测试中的一个位置更改它.

也许您应该考虑在单元测试中编写通用基类,或者为测试使用"setup"例程.