Transient 注入 Singleton 有什么问题?

What is wrong when Transient injected to Singleton?

Mark Seemann 名为 Captive Dependency 的 DI 容器存在未正确配置的问题。 很明显,例如,当 "PerCall" 依赖项被注入 "Singleton" 时。 但是当 "Transient" 被注入 "Singleton"?我不太清楚为什么我不应该在某些情况下特别这样做,例如注册为瞬时注入到单例服务中并永远存在的计时器。

SimpleInjector DI容器中有Lifestyle Mismatches diagnostic warning。描述说:

it is safe for a transient component to depend on a singleton, but not the other way around

为什么不好?在这种情况下,坏事的一个很好的例子将非常有用。 当它 "always" 不好或某些场景可以被允许时是否有任何限制(参见我上面的示例)?

这是不安全的,因为你的瞬态实例将永远在单例中。

示例:您可以将单例注入另一个 class 实例。在那种情况下,您也将间接注入瞬态实例。例如,您可能会遇到 thread-safety 问题。

如果您将 class 注册为单例,您将其视为 thread-safe,因为您可以在几个线程中同时使用同一个实例。如果您将 class 注册为瞬态,很可能它是轻量级的而不是 thread-safe class。这就是为什么您将其注册为瞬态而不是单例的原因,对吗?这就是您看到警告的原因:同时使用瞬态对象是不安全的。在 asp.net 核心嵌入式 IoC 中,您将在这种情况下面临运行时异常。

想象一下:您的瞬态 class 在内部创建 SqlConnection 并在其生命周期内保持打开状态并允许在不同步的情况下使用它。这没关系,因为它被认为在一个线程中存活了很短的一段时间。然后你将它注入单例,然后在每个请求中使用这个单例。

这取决于您对“瞬态”实际含义的定义。简单注入器文档 states:

Simple Injector considers Transient registrations to be lasting for only a short time; temporary, i.e. short lived and not reused. For that reason, Simple Injector prevents the injection of Transient components into Singleton consumers as they are expected to be longer lived, which would otherwise result in Lifestyle Mismatches.

其他一些 DI 容器对“瞬态”使用不同的定义。例如,.NET Core DI 容器(MS.DI)建议您的瞬时注册为“lightweight, stateless services.”,因为它们被假定为无状态的,将它们注入任何其他生命周期的消费者是安全的,因为只要它们自己没有任何状态(子)依赖项。在 MS.DI 的上下文中,“有状态”通常表示范围内的依赖项。 MS.DI 对瞬态的定义与 Autofac 所称的 Instance Per Dependency 相同。 IMO,Autofac的命名更正确,因为从概念上讲,transient的两种定义有很大的区别,我相信大多数DI Container都遵循“lasting for only a short time; temporary "定义。

只要您的 Transient 组件是 stateless 并且只要该组件不包含有状态的依赖项,将其注入到单例(或作用域)消费者中就没有坏处。然而,将这种无状态组件注入到单例中,仍然会使该组件长寿命,这与短寿命完全不同。由于 Simple Injector 不知道您的组件是否包含状态,它认为所有瞬变都是短暂的,因此会警告您将瞬变注入单例。

更新:默认情况下,Simple Injector v5 现在允许将 Transient 组件注入 Scoped。对此进行了更详细的讨论 here