依赖注入:单个类(WCF服务)有多个依赖(DB存储库)如何处理?

Sas*_*sha 2 c# dependency-injection

我已经读过Mark Seemann的一本书".NET中的依赖注入",它让我看到很多东西.但仍然很少有问题.这是其中之一:

假设我们有一个WCF服务公开API用于处理某些数据库:

public class MyService : IMyService
{
    private ITableARepository _reposA;
    private ITableARepository _reposB;
    //....

    public IEnumerable<EntityA> GetAEntities()
    {
        return _reposA.GetAll().Select(x=>x.ToDTO())
    }

    public IEnumerable<EntityB> GetBEntities()
    {
        return _reposB.GetAll().Select(x=>x.ToDTO())
    }

    //...
}
Run Code Online (Sandbox Code Playgroud)

可能有数十个存储库服务依赖.一些方法使用一个方法,一些方法另一个方法,一些方法使用少量存储库.

我的问题是如何正确组织存储库依赖注入服务

我看到的选项:

  1. 构造函数注入.用几十个参数创建一个巨大的构造函数.易于使用,但很难管理参数列表.此外,它对性能的影响非常大,因为即使它不使用单独的数据库连接,每个未使用的存储库也会浪费资源.
  2. 物业注入.优化性能,但使用率变得不明显.服务的创建者应该如何知道为特定方法调用初始化哪些属性?此外,此创建者应该对每个方法调用都是通用的,并且位于组合根中.因此逻辑变得非常复杂且容易出错.
  3. 有点非标准(书中没有描述)方法:创建一个存储库工厂并依赖它而不是具体的存储库.但是这本书说工厂经常被错误地用作克服问题的一种方法,这些问题可以通过适当的DI使用得到更好的解决.因此,这种方法对我来说是可疑的(同时实现了绩效和透明度目标).

或者这个关系存在一个概念性的问题1到多个依赖关系?

我假设答案应该根据服务实例上下文模式而有所不同(可能是它的Single实例,构造函数注入很好;对于PerCall选项3看起来最好忽略上面的警告;对于perSession,一切都取决于会话生命周期:是否更多接近单实例或PerCall).

如果它确实依赖于实例上下文模式,那么就很难对其进行更改,因为更改需要对代码进行大的更改(从构造函数注入转移到属性注入或存储库工厂).但是WCF服务的整个概念确保了更改实例上下文模式很简单(并且我不太可能需要更改它).这让我对DI和WCF组合更加困惑.

谁能解释一下这个案子应该如何正确解决?

Ste*_*ven 5

用几十个参数创建一个巨大的构造函数

您不应该使用大量构造函数参数创建类.这是构造函数的过度注入代码 - 气味.拥有大量参数的构造函数表明这样的类太多了:违反单一责任原则.这导致代码难以维护和扩展.

此外,它对于性能非常糟糕,因为每个未使用的存储库都是浪费资源

你测量过这个吗?构造函数参数的数量应该主要是对应用程序的性能不敬.这不应该导致任何明显的性能差异.如果确实如此,那么现在应该看看构造函数的工作量(因为注入构造函数应该很简单),或者如果构造函数很简单,则需要时间切换到更快的DI容器.创建一堆服务类通常应该非常快.

即使它不使用单独的DB连接.

构造函数不应该首先打开连接.再说一遍:它们应该很简单.

物业注入.优化性能服务的创建者应该如何知道要为特定方法调用初始化哪些属性

调用者无法可靠地确定需要哪些依赖项,因为通常只需要构造函数参数.需要属性会导致时间耦合,并且您将失去编译时支持.

由于调用者无法确定需要哪些属性,因此需要注入所有属性,这使得性能与构造函数注入等效,我说 - 根本不应该成为问题.

有点非标准(书中没有描述)方法:创建一个存储库工厂并依赖它而不是具体的存储库.

您可以注入存储库提供程序,而不是注入存储库工厂,这种模式更好地称为工作单元模式.工作单元可以提供对存储库的访问.

我假设答案应该根据服务实例上下文模式而有所不同

不,因为您永远不应该使用WCF"单一"模式.在大多数情况下,您注入WCF服务的依赖项不是线程安全的,不应该超过单个请求.将它们注入单独的WCF服务会导致强制依赖,这很糟糕,因为它会导致各种并发错误.

这里的核心问题似乎是您的WCF服务类很大并且违反了单一责任原则,导致它们难以创建,维护和测试.通过以下方式修复此违规:

  1. 将它们分成多个较小的类,或者
  2. 将功能从它们移动到聚合服务中并应用模式,例如命令/处理程序查询/处理程序模式.