2

我有以下类,后者服务类与特定容器耦合,但如果我使用前者,为什么控制器类应该注入服务类依赖项而不是仅仅使用服务类并将其留给服务类来解决依赖关系?

public class ProfileController {

    public ProfileController(IProfileService profileService, IProfileRepository  profileRepository, IUnitOfWork unitOfWork, IDatabaseFactory databaseFactory) {
    profileService.Get();
    }
}

public class ProfileService:IProfileService {

    public ProfileService(IProfileRepository  profileRepository, IUnitOfWork unitOfWork, IDatabaseFactory databaseFactory) {
    profileService.Get();
    }  
}

或者

public class ProfileController {
    public ProfileController(IProfileService profileService) {
                            profileService.Get();
    }
}

public class ProfileService:IProfileService {

    public ProfileService() { 
        var dbfactory=container.Resolve<IDatabaseFactory>();
        var unitofwork=container.Resolve<IUnitofWork>();
        var rep=container.Resolve<IProfileRepository>();
        rep.Get();
    }  
}
4

2 回答 2

1

服务类不应该解决它的依赖关系。所以你应该选择选项 1。实例应该与 DI 容器完全无关。

为什么?

关注点分离:依赖注入的主要思想是将对象的实例化视为一个不同的关注点,它在一个地方处理:容器。创建实例并在它们之间设置引用是容器的工作。当您让实例本身使用容器时,您会破坏这个概念。

模块类的可重用性:当你有一个“使用”DI容器的类时,你不能再在不同的架构中使用这个类,包括不同的DI框架,而不改变它。尽管您可能不想这样做,但您没有充分的理由限制自己。

语义清晰:您的构造函数参数应回答以下问题:创建该类的实例需要什么。在你的情况下,这是东西:IDatabaseFactory等,而不是什么(())。

可测试性:你不希望你的类需要整个 DI 基础设施才能工作。也许出于测试原因,模型应该做的,在运行时由您的 DI 容器完成。这与“可重用性”有关。

快速失败:假设依赖项不可解析:在构建容器时抛出异常而不是在实例化类时抛出异常是很好的。

摘要

container.Resolve(...)

不应出现在您的模块中。

于 2013-03-13T14:28:33.790 回答
0

我觉得这两个例子都偏离了轨道。

建议的解决方案:

public class ProfileService:IProfileService {

  public ProfileService(IProfileRepository  profileRepository, IUnitOfWork unitOfWork, IDatabaseFactory databaseFactory) {

  }  
}

通过以下方式在调用者中使用该服务。

public class ProfileController {
  [ConstructorInjection]
  public ProfileController(IProfileService profileService) {

  }
}

或者

public class ProfileController {
  public ProfileController(IProfileService profileService) {

  }
}

或者

public class ProfileController {

  public ProfileController() {
       this.service = resolveFromContainer.Get<IProfileService>();
  }
}

或者

public class ProfileController {

  public ProfileController() {
       this.service = new ServiceFactory.get<IProfileService>()
  }
}

(1)。在上述任何一种情况下,我都不认为关注点分离是一个问题。控制器没有执行存储库工作。它仅提供要使用的存储库

就暴露模块而言。对于共享程序集,我同意 100%我们不应该强制调用者使用 di 容器。

但是,我们可以构建共享代码并让使用应用程序选择如何注入共享模块依赖项。

(2).Marc 在他的回答“语义清晰度:您的构造函数参数应该回答以下问题:我需要什么来创建该类的实例。在您的情况下,这是东西:IDatabaseFactory 等,而不是什么(())。”

我同意 100%,但是如果你看到控制器构造函数。它不仅有服务,而且还有很多其他的东西。我需要其他所有东西才能让我的控制器工作吗?这是一个特定于应用程序的问题和答案,不能泛化。如果您只需要服务工作,则将其传入。不要传递服务依赖项,因为它没有定义控制器。它定义了服务。

传递存储库不同于将开发人员密钥传递给库。尽管您允许灵活地提供存储库,但您也指定了目标。这在某些情况下很有用。想想存储日志信息的库(我们正在指定存储位置)在代码被抽象出来的 RequestProcessor 模式中也很有用。

问:如果您希望您的服务保存并返回,那么将存储库,dbFactory 传递给它是否正确?

在上面的新解决方案中。配置文件服务代码不会更改。配置文件服务可能需要数据库、存储库但控制器是否需要所有存储库、工厂、服务才能工作?这是特定于应用程序的

* Q:传入的参数是创建控制器所需要的,还是间接用来创建下面的其他依赖?*

(3) “container.Resolve(...) 不应出现在您的模块中。”

* Q:你指的模块是什么?你是说我们应该使用 DI 容器,但不知何故从不选择在任何一段代码中使用 DI 属性/解析方法/依赖配置?*

(我可以看到 di container 的作者对这种想法感到畏缩)

问:你能回答,在哪里可以使用resolve方法?

我 100% 同意共享代码不应使用解析方法。然而,控制器可以作为应用程序特定的代码,并且确实需要容器来注入/组装其依赖项。

DI 容器是一个核心框架,它是应用程序设计的一等公民。如果我们还没有选择一个 DI 容器来构建,我们真的接受了 DI 吗?

如果 DI 容器是 .NET 库之类的一等公民,那么您永远不会“计划”在没有额外工作的情况下更改容器。寻找可以轻松切换 DI 容器的代码的工作量太大,最好留给微软/社区来解决。

摘要: 我同意 Marc 对答案的大部分想法*但是,任何一种解决方案提出的答案都不能正确回答他提出的所有问题。*

参考:

  1. http://martinfowler.com/articles/injection.html
  2. http://msdn.microsoft.com/en-us/magazine/dd882510.aspx

小抱怨:对上述任一解决方案关于使用工作单元/存储库的小评论

上面链接中的引用代码。public void RunCommands(Invoice invoice) { IUnitOfWork unitOfWork = _unitOfWorkFactory.StartNew();

UnitOfWorkfactory 应该管理数据库工厂并且不应该传递给服务。

恕我直言

我们正在做同样的依赖关系,这对我来说感觉不对。这同样适用于存储库工厂。

工厂应该设置上下文,如数据库上下文等,责任不应该在服务中。

这可能是更好的接口 public ProfileService(IProfileRepository profileRepository, IUnitOfWork unitOfWork)

于 2013-03-13T18:44:58.290 回答