【问题标题】:DI Singleton instance vs Transient instanceDI 单例实例与瞬态实例
【发布时间】:2018-06-25 12:50:20
【问题描述】:

几年前,IoC 性能指南指出,IoC 容器应仅用于解析长期存在的实例(基本上是单例),而瞬态类型对象应使用单例工厂(由容器保存)创建。

我现在正在阅读有关 ASP.NET Core 的信息,并且我看到的几个示例将瞬态生命周期用于注入的对象。瞬态现在是提供静态方法(并且是无状态)的服务的首选方法,是否发生了一些变化?

【问题讨论】:

  • “几年前 IoC 性能指南已声明”。你有这个的来源吗?据我所知,这不是一个共同的指导方针。 The book 肯定不建议这样做。
  • 我想大多数人都发现,与必须确保单例永远不会捕获生命周期较短的服务相比,不断创建新服务对象的性能开销并不是什么大问题.将所有事情都从短暂的开始更容易,并且只有在有令人信服的理由时才会偏离。
  • @StriplingWarrior 我的经验实际上是相反的。制作完整的对象图单例具有消除许多常见陷阱(如 Captive Dependencies)的奇妙好处,并迫使您进入严格的无状态模型。我遇到过我周围的其他人发现 Singleton 是一种更直观的模型,尤其是对于没有 DI 经验的开发人员。它确实需要通过环境状态存储有状态对象(如 DbContext),但仍然比查找 Captive Dependencies 简单得多。
  • @Steven:有趣的是,您发现单例减少了强制依赖的发生。当我过去使用默认单例时,我经常有捕获依赖项,因为 dependent 类必须意识到其依赖项的生命周期(一种紧密耦合的形式),而瞬态-默认意味着只有需要更长生命周期的类才需要避免捕获它们的依赖项。我还发现瞬态作用域强制使用无状态模型,而单例作用域会诱使开发人员在私有字段中维护状态。
  • 我的发现完全相反。单例不需要私有字段中的状态,尽管它确实需要环境状态作为组合根的一部分。我的经验是,开发人员检测有状态服务(单例存在问题)比检测 Captive Dependencies 要容易得多。

标签: c# .net dependency-injection inversion-of-control


【解决方案1】:

“长寿命实例”的概念并没有说明它们的寿命或每次看到的生活方式,而是从消费者的角度来看,它们只有一个实例。它们是无状态的

换句话说,“长期实例”指的是服务依赖项,而“短期实例”指的是以数据为中心的对象,例如实体、DTO、消息和视图模型。

这些服务由您的Composition Root(通常但不一定是您的 DI 容器)创建和管理,而以数据为中心的对象由应用程序代码直接管理。换句话说,那些“长寿命对象”是由合成根“更新”的,而“短寿命对象”是由应用程序代码本身更新的。

这些以数据为中心的对象是易变的,它们通常只在请求期间(甚至更短)存在,尽管它们可能会被缓存并在应用程序存在时存在。

依赖关系也可以存在很短的时间,但通常是在请求期间。

【讨论】:

  • 我怀疑这就是 OP 所说的“短期实例”的意思,因为他特别提到它们应该使用工厂创建,这对于创建 DTO 等来说不是很常见的做法。
  • 你可能是对的。我在这里猜测了一下,因为这个问题没有提到来源。我现在认为 OP 实际上是指first edition of Dependency Injection in .NET,它实际上描述了工厂如何处理短期依赖项(第二版现在实际上不再强调工厂的使用)。但是让我们等待 OP 发布他的消息来源,这样我们就可以将他的问题放在上下文中。我会根据他的消息来源更新我的答案或发布一个新答案。
  • 感谢您的周到回答!不幸的是,我在途中发现了“IoC 性能指南”,但我不记得确切的来源。在这一点上,它只在我的记忆中。关于工厂如何被淡化的有趣点。我将不得不通读那些段落。在讨论这个问题的页面上有任何指针吗?
  • 您好 John,section 6.2 Abuse of Abstract Factoriessecond edition 致力于此主题。
  • 关于性能,早期的依赖注入框架可能足够慢,可以通过使用单例绑定来显着改进(例如,每个 Web 请求可能需要 50 毫秒)。我可以看到有人建议单例来提高性能。但是像 SimpleInjector 这样更现代的框架要快得多。 SimpleInjector 还在启动时检测 Captive Dependencies,这非常有用。
【解决方案2】:

什么都没有改变。

我不确定你在哪里读到的,但do note that a service cannot have a lifetime shorter than the services that depend on it。因此,如果您将服务注入到单例中,那么您的想法是正确的——在这种情况下,您可能需要一个工厂才能正确释放实例。

但是,由于 ASP.NET Core 会在每个请求上解析控制器实例,因此注入瞬态依赖项是可以的,因为当控制器被销毁时,它将超出范围。

【讨论】:

  • 明白。我想我现在最关心的是性能。每次请求时都必须创建每个瞬态实例,因此我认为与使用单例相比,这会损害性能。
  • 是的。但是,如果性能是一个问题(即控制器可以有一个与其他服务共享的单例依赖项),那么没有什么能阻止你注册一个生命周期长于的依赖项。无论哪种方式,.NET 运行时都经过优化,可以像 constructor is simple 一样高效地创建实例,但有时创建单例可能有益的依赖项代价高昂。单例可能需要更复杂,因为它们通常必须是线程安全的。
猜你喜欢
  • 2019-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多