在Simple Injector中,为什么单例或作用域服务依赖于瞬态服务是一个错误

时间:2015-10-12 12:25:25

标签: c# dependency-injection simple-injector

我使用简单的进样器3.0.4

我有一项服务,其生活方式取决于具有短暂生活方式的服务。

当我调用container.Verify()时,我收到有关生活方式不匹配的诊断错误。

导致问题的瞬态服务被注入到其他临时服务中,因此在我开始使用整个项目范围之前我需要问一下。为什么从任何生活方式的范围到瞬态的依赖都是一个问题?每次注射瞬态时都会新鲜出现,所以没有其他任何东西可以干扰它。本质上,瞬态对象的生命周期由它注入的服务来控制。

此外,我已经从here阅读了有关此主题的文档,我确实理解为什么您不希望单例依赖于作用域服务,但是对瞬态的依赖肯定是安全的?

1 个答案:

答案 0 :(得分:11)

  

每次注射瞬变时都会新鲜,所以没有其他任何东西可以干扰它。

每次从容器中请求瞬态时,都会新建瞬态,但是一旦将它们注入组件中,只要该组件存在,它们就会一直存在。因此,如果消费组件是单例,这意味着它将拖拽其所有依赖项,使它们实际上也是单例。当您了解如何实现依赖注入时,这种行为就变得很明显了:

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不包含瞬态生活方式。相反,它具有InstancePerDependency生活方式。从技术上讲,这与瞬态相同,但意图却截然不同。用InstancePerDependency你说:“这个组件只要它的消费者就可以活着,无论生活方式如何”。可能有些情况下这是有道理的,但通过这样做,你实际上忽略了房间里的大象,我已经看到缺乏检测是一个常见的错误来源。在大多数情况下,如果您不关心组件生活方式,则意味着它应该注册为单身 - 而不是InstancePerDependency

Simple Injector不允许将瞬态注入到作用域实例中的原因是因为作用域实例也可以存活很长时间(取决于应用程序)并且您不能总是假设可以安全地注入瞬态进入范围内的消费者。

最后,它是关于传达代码意图的全部内容。如果组件是无状态或线程安全的,则应将其注册为单例。它不是线程安全的,你将它注册为作用域或瞬态。这使得任何读取配置的人都清楚如何处理这样的组件,它允许Simple Injector为您检测任何错误配置。

当Simple Injector为您检测到错误配置时,我得出的结论是,当您的系统围绕纯粹由单件组件组成的对象图设计时,您的DI配置可以大大简化。我表达了这些想法here。这将消除我们在使用依赖注入时遇到的许多复杂性,甚至比DI本身已经更快地暴露SOLID原则违规。

  

在我开始之前让我的整个项目成为范围

这是我不建议做的事情。您通常会看到应用程序中只有一些“叶子组件”是作用域的(例如DbContext)。这些范围内的组件不依赖于许多其他组件。您自己编写的组件通常应该是无状态的,不需要任何缓存。因此,如果使对象图单例(尚未)成为一个选项,我通常会尽可能多地生成对象图瞬态,并且只有少数叶组件作用域。由于瞬态可以安全地依赖于作用域实例,因此一切都将按预期工作。