如何重新设计C#应用程序以避免构造函数注入过载?

如何重新设计C#应用程序以避免构造函数注入过载?,c#,unit-testing,web-services,dependency-injection,moq,C#,Unit Testing,Web Services,Dependency Injection,Moq,我还不熟悉DI和单元测试。我的任务是在一些遗留代码中添加单元测试。我正在开发一个WCF web服务。必须进行大量重构。怪物类被分为不同的类,这是有意义的。怪物方法拆分为多个方法。最后,为外部依赖项创建接口类。这样做最初是为了便于模拟单元测试的依赖关系 正如我所说的那样,依赖关系的列表不断增长。我还有其他web服务要调用,SQL服务器和DB2数据库要交互,配置文件要读取,日志要写入,以及Sharepoint数据的读取。到目前为止,我有10个依赖项。每次我添加一个,它就会破坏我所有的单元测试,因为构

我还不熟悉DI和单元测试。我的任务是在一些遗留代码中添加单元测试。我正在开发一个WCF web服务。必须进行大量重构。怪物类被分为不同的类,这是有意义的。怪物方法拆分为多个方法。最后,为外部依赖项创建接口类。这样做最初是为了便于模拟单元测试的依赖关系

正如我所说的那样,依赖关系的列表不断增长。我还有其他web服务要调用,SQL服务器和DB2数据库要交互,配置文件要读取,日志要写入,以及Sharepoint数据的读取。到目前为止,我有10个依赖项。每次我添加一个,它就会破坏我所有的单元测试,因为构造函数中有一个新参数

如果有帮助,我将使用.NET4.5、CastleWindsor作为我的IOC、MSTest和Moq进行测试

我看了这里,但这并没有提供任何真正的解决方案。我只想说“你们的类可能做得太多了。”我查看了Facade和聚合服务,但这似乎只是改变了现状

因此,我需要一些帮助,了解如何使这个类“更少”,但仍然提供相同的输出

public AccountServices(ISomeWebServiceProvider someWebServiceProvider,
        ISomeOtherWebProvider someOtherWebProvider,
        IConfigurationSettings configurationSettings,
        IDB2Connect dB2Connect,
        IDB2SomeOtherData dB2SomeOtherData,
        IDB2DatabaseData dB2DatabaseData,
        ISharepointServiceProvider sharepointServiceProvider,
        ILoggingProvider loggingProvider,
        IAnotherProvider AnotherProvider,
        ISQLConnect SQLConnect)
    {
        _configurationSettings = configurationSettings;
        _someWebServiceProvider = someWebServiceProvider;
        _someOtherWebProvider = someOtherWebProvider;
        _dB2Connect = dB2Connect;
        _dB2SomeOtherData = dB2SomeOtherData;
        _dB2DatabaseData = dB2DatabaseData;
        _sharepointServiceProvider = sharepointServiceProvider;
        _loggingProvider = loggingProvider;
        _AnotherProvider = AnotherProvider;
        _SQLConnect = SQLConnect;
    }
几乎所有这些都在其他组件中,但我需要能够在主应用程序中使用它们,并在单元测试中模拟它们

下面是其中一种方法的布局

public ExpectedResponse GetAccountData(string AccountNumber)
    {
          // Get Needed Config Settings
          ...
          // Do some data validation before processing data
          ...
          // Try to retrieve data for DB2
          ...
          // Try to retrieve data for Sharepoint
          ...
          // Map data to response.
          ...
          // If error, handle it and write error to log
    }
其他方法非常类似,但它们可能涉及SQL Server或一个或多个web服务

理想情况下,我想要的是一个应用程序的示例,它需要很多依赖项,具有单元测试,并且避免了不断向构造函数添加新的依赖项,从而导致您更新所有单元测试以添加新参数


谢谢

我不确定这是否有帮助,但我提出了一个名为GetTester的模式,它包装了构造函数,使处理参数变得更简单。下面是一个例子:

 private SmartCache GetTester(out Mock<IMemoryCache> memory, out Mock<IRedisCache> redis)
 {
     memory = new Mock<IMemoryCache>();
     redis = new Mock<IRedisCache>();
     return new SmartCache(memory.Object, redis.Object);
 }

如果您有构造函数更改,这些仍然会中断,但是您可以创建重载以最小化对测试的更改。这是一件麻烦事,但比其他方式要容易。

因此,您的课程可能做得太多了。如果你发现你在不断地增加一个类正在做的工作,并且结果需要提供额外的对象来帮助这些额外的任务,那么这可能是问题所在,你需要考虑分解工作。


但是,如果不是这样,那么另一种选择是让类引用依赖类,该依赖类提供对实现各种接口的实例化具体对象或可用于构造对象的实例化工厂对象的访问。然后,你不必不断地传递新参数,只需传递单个对象,然后根据需要从中拉出或创建对象。

你链接的帖子是正确的,你的类做得太多了。忽略依赖项,像
AccountServices
这样的名称本身就是巨大的危险信号,表明您的类有太多的责任。太多了。您的所有数据库内容都可以抽象为另一个专门处理数据库操作的类,并且只将该类作为依赖项传递,而不是在一个不了解持久存储如何工作的类中传递与db interop相关的10个依赖项。您是否可以创建一个包含所有这些或您考虑过封装吗?在单元测试中使用依赖项注入框架为您创建
AccountServices
。为清楚起见,该服务没有直接进行任何数据库调用,这与单独的类/项目现在所做的是一样的。所以我只是在依赖项中调用一个方法来获取数据,在某些情况下插入数据。但我仍然需要嘲笑这些事情。即使我将所有的数据依赖性合并成一个只会稍微降低数量的依赖项。我认为这些数据类做的太多了。如果你认为你的类没有做“太多”,那么拥有这么多的依赖关系是完全好的。此外,每次注入新实例时都要更新单元测试是不可能的。这就是你测试的重点。它们应该失败。你应该找出原因并更新它们。我还创建了一个名为CreateGetTesterMethod的实用程序。有了这个,我将那个大构造函数复制到剪贴板,运行这个实用程序,剪贴板的内容被替换为该类的GetTester方法。这真的很有帮助。不幸的是,代码不是我的。而且,坚持到底!我最近为DI和单元测试重构了90000行代码库,实现了100%的代码覆盖率。这是可以做到的!(使用管理支持:D)这完全忽略了一个事实,即关于帐户的类不应该实现数据库CRUD逻辑。这并不能解决问题,只是隐藏了问题。我给了他一个变通方法,让他更容易处理遗留代码库。在理想情况下,他会放弃一切,从头开始重写应用程序。但这不是我们生活的世界。我并不反对这一点,但我发现很难理解为什么不使用DI框架来处理这么多依赖性。
 SmartCache cache = GetTester(out Mock<IMemoryCache> memory, out Mock<IRedisCache> redis);
 SmartCache cache = GetTester(out _, out _);