关于c#:在Simple Injector中,为什么单例或作用域服务依赖于瞬态服务是错误的

In Simple Injector why is it an error for a singleton or scoped service to depend on a transient service

我用的是简易注射器3.0.4

我有一个服务,它的生活方式是范围依赖于一个服务,它的生活方式是短暂的。

当我调用container.verify()时,会得到一个关于生活方式不匹配的诊断错误。

引起问题的临时服务被注入到其他临时服务中,所以在我继续进行并使整个项目范围达到我需要问的范围之前。为什么从一种生活方式的范围依赖于暂时的问题?瞬变每次被注入时都是新鲜的,所以没有其他东西能干扰它。从本质上讲,瞬时对象的生命周期由其注入的服务控制。

另外,我已经从这里阅读了有关这个主题的文档,我确实理解了为什么您不希望单例依赖于一个作用域服务,但是对瞬时的依赖总是安全的?


Transients are newed up fresh each time they are injected so nothing else can interfere with it.

每次从容器中请求瞬变时,都会更新它们,但是一旦它们被注入到组件中,它们就会在组件寿命内一直存在。因此,如果消费组件是一个单例组件,这意味着它将拖动所有依赖项,使它们实际上也是单例组件。当您查看通常如何实现依赖项注入时,这种行为会变得明显:

1
2
3
4
5
6
7
8
9
10
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使用相同的依赖项,它们就会保持活动状态。

In essence the lifetime of a transient object is governed by the service it is injected into.

准确地说,一个组件的生活方式至少和它的消费者一样长。然而,依赖关系可以有多个具有不同生活方式的消费者,这使得很难了解依赖关系的寿命。当注入到使用者1中时,它可能在请求的持续时间内存在,而注入到使用者2中的该依赖项的另一个实例将与应用程序的持续时间相同。

与作用域实例一样,临时注册通常不是线程安全的;否则,您将把它们注册为singleton。将瞬变保持较长时间,显然会导致并发错误或与陈旧数据相关的错误。这就是为什么简单注入器默认情况下不允许这样做并抛出异常的原因。

与简单的注入器相比,autopac如何定义其生活方式可能会让您困惑。自动空调不包含短暂的生活方式。相反,它有一种InstancePerDependency的生活方式。从技术上讲,这与暂时性相同,但目的却截然不同。对于InstancePerDependency,你会说:"这个组件的寿命和它的消费者一样长,不管它的生活方式是什么。"有些情况下这是有道理的,但这样做实际上是忽略了房间里的大象,我已经看到缺乏检测是常见的虫子来源。在大多数情况下,如果您不关心组件的生活方式,这意味着它应该注册为singleton,而不是InstancePerDependency

简单注入器不允许将瞬变注入作用域实例的原因是,作用域实例也可以存活很长时间(取决于应用程序),并且不能总是假设瞬变可以安全地注入作用域使用者。

最后,这一切都是为了传达代码的意图。如果一个组件是无状态的或线程安全的,您应该将它注册为singleton。它不是线程安全的,您将它注册为作用域或瞬时的。这使任何阅读配置的人都能清楚地了解他应该如何处理这些组件,并且它允许简单的注入器为您检测任何错误配置。

虽然简单的注入器为您检测错误配置,但我得出的结论是,当您的系统是围绕纯粹由单例组件组成的对象图设计时,DI配置可以大大简化。我在这里表达了这些想法。这将消除我们在处理依赖注入时面临的许多复杂性,甚至比DI本身更快速地暴露出固体原则的违反。

before I go ahead and make my whole project scoped

这是我不建议做的事。您通常会看到,只有应用程序中的一些"叶组件"具有范围(如DbContext)。这些作用域组件不依赖于许多其他组件。您自己编写的组件通常应该是无状态的,不需要任何缓存。因此,如果将对象图设为单件(还没有)是一个选项,那么我通常会使对象图的大部分都是暂时的,并且只有少数几个叶子组件的作用域。因为瞬变可以安全地依赖于作用域实例,所以一切都将按预期工作。