瞬态在每次注入时都会更新,因此没有其他东西可以干扰它。
每当您从容器中请求它们时,都会更新瞬态,但是一旦将它们注入到组件中,只要该组件存在,它们就会一直存在。所以如果消费组件是一个单例,这意味着它将拖拽它的所有依赖项,使它们实际上也是单例的。当您查看通常如何实现依赖注入时,这种行为会变得很明显:
public class SomeComponent
{
private readonly ILogger logger;
private readonly IService service;
public SomeComponent(ILogger logger, IService service) {
this.logger = logger;
this.service = service;
}
}
如您所见,依赖项存储在组件的私有字段中,只要SomeComponent 存在并且SomeComponent 将继续使用相同的依赖项,它们就会保持活动状态。
本质上,瞬态对象的生命周期由它注入的服务控制。
没错;组件的生活方式将至少与其消费者一样长。但是,一个依赖项可以有多个具有不同生活方式的消费者,这使得很难看出依赖项会持续多久。当注入到消费者 1 中时,它可能会在请求期间存在,而该依赖项的另一个实例注入到消费者 2 中时,将与应用程序一样存在。
就像作用域实例一样,瞬态注册通常不是线程安全的;否则,您会将它们注册为单身人士。让瞬态保持更长的时间,显然会导致并发错误或与陈旧数据相关的错误。这就是 Simple Injector 默认不允许这样做并抛出异常的原因。
与 Simple Injector 相比,您可能会对 Autofac 如何定义其生活方式感到困惑。 Autofac 不包含 Transient 生活方式。相反,它具有InstancePerDependency 的生活方式。从技术上讲,这与瞬态相同,但意图却大不相同。对于InstancePerDependency,您会说:“该组件旨在与它的消费者一样长寿,无论这种生活方式可能是什么”。可能在某些情况下这是有道理的,但这样做实际上是在忽略房间里的大象,而且我发现缺乏检测是常见的错误来源。在大多数情况下,如果你不关心组件的生活方式,这意味着它应该被注册为单例——而不是InstancePerDependency。
Simple Injector 不允许将瞬态注入到作用域实例中的原因是,作用域实例也可以存在很长时间(取决于应用程序),并且您不能总是假设可以安全地注入瞬变进入范围消费者。
最后,一切都是为了传达代码的意图。如果组件是无状态或线程安全的,则应将其注册为单例。它不是线程安全的,您将其注册为作用域或瞬态。这让任何阅读配置的人都清楚他应该如何处理此类组件,并且它允许 Simple Injector 为您检测任何错误配置。
虽然 Simple Injector 会为您检测到错误配置,但我得出的结论是,当您的系统围绕纯粹由单例组件组成的对象图进行设计时,您的 DI 配置可以大大简化。我表达了这些想法here。这将消除我们在使用依赖注入时面临的许多复杂性,甚至比 DI 本身已经更快地暴露违反 SOLID 原则的行为。
在我开始确定我的整个项目范围之前
我不建议这样做。您通常会看到应用程序中只有一些“叶组件”是作用域的(例如DbContext)。这些作用域组件不依赖于许多其他组件。您自己编写的组件通常应该是无状态的,并且不需要任何缓存。因此,如果使对象图成为单例(还不是)一种选择,我通常会尽可能多地使对象图成为瞬态,并且只有少数叶组件的范围。由于瞬态可以安全地依赖于作用域实例,所以一切都会按预期工作。