说明

不出所料,每个解析对象内都被注入了一个属于他们自己的唯一的token。看,依赖注入起作用了!

我们可以看到几点有趣的地方:

  • SingletonResolvable的token是从根域内(root scope)解析出的

  • 其他解析类的token全部是从一个叫AutofacWebRequest的域内解析出的

如下图所示:

PerDependency1

出于好奇,我们来看下如果再调用一次接口会发生什么:

PerDependency_2

Token #1没有变。这是因为根域的生命周期和程序是保持一致的。换句话说,SingletonResolvable对象以及它所依赖的ScopeToken对象将一直存在,直到程序停止运行为止。

相反,Tokens #2, #3 和 #4已经全部被释放掉了,因为AutofacWebRequest域的生命周期是和Web请求保持一致的。也就是,该域在请求发起时被创建,当请求结束后就立即被释放掉了。

全局单例(SingleInstance)

再次不出所料地,每个解析对象获得的都是同一个ScopeToken实例。

SingleInstance1

有两点需要指出:

  1. 所有单例都处于根域内,并且,上面已经说过,根域的生命周期和程序一样长。
  2. AutoFac解析组件时,会依次向上到其父类域内查找依赖

域内单例(PerLifetimeScope)

正如预期的,我们有两个“激活”的域,而且每个域内都有一个ScopeToken实例。

PerLifetimeScope1

让我们来看下当再次调用接口会发生什么:

PerLifetimeScope_2

和之前的瞬时单例一样,处在根域内的Token #1一直存在着,而处在AutofacWebRequest域内的Token #2在请求结束后被释放掉了。

一直以来有一个普遍的错误认知,就是认为在WebAPI项目中如果组件被注册为域内单例(InstancePerLifetimeScope)的话,那么意思就是它将存活在一次request请求内,即它的生命周期就是一次request请求的生命周期。但是正如上面的例子所展示的,这种认知是错误的。

被注册为域内单例的组件,它的生命周期是由解析它的域所决定的。

因为SingletonResolvable实例是在根域内解析它的token,所以这个token实例就存在于根域内,而不是一次web请求的生命周期域。之前已经说过,这里我要再重复一遍:这个token会一直存在直到整个应用程序停止运行为止(即IIS工作进程被回收时)。任何对象只要是在根域内要求获取依赖的ScopeToken,那么它就会得到这个唯一单例的对象。

Api请求内单例(InstancePerApiRequest)

请求出现了一个令人不快的异常,内容是:

被请求获取的实例所在的域内,找不到一个标签为‘AutofacWebRequest’的域。这通常表明,有一个被注册为每次HTTP请求内单例的组件被一个全局单例的组件请求获取(或者是类似的其他场景)。web项目通常是从DependencyResolver.Current或者ILifetimeScopeProvider.RequestLifetime中获取依赖,但是不允许直接从根容器中获取。

为了明白为什么会发生这样的异常,我们需要回到AutoFac的技术文档上来。里面说,Api请求内单例(InstancePerApiRequest)实际上是每个匹配域内单例(InstancePerMatchingLifetimeScope)的一种特殊情况,文档原文是这样的 :

用Api请求内单例来注册组件,那么每个依赖组件或者每次通过Resolve()解析,只要是在打了统一标签名称的域内,就会得到同一个对象,即它们共享同一个单例。在这个特定标签域下面的所有子域中,依赖组件也会共享其父域中的单例。如果在当前域和它的父域中都找不到这个标签域,那么一个类型为DependencyResolutionException的异常将会被抛出。

具体来说,Api请求内单例(InstancePerApiRequest)实质上是在一个特定标签域内单例,正如你所猜测的,这个特定标签域就是AutofacWebRequest域。这个域会在一次请求开始时被创建,并且在请求结束后被立即释放。综上,如果使用Api请求内单例(InstancePerApiRequest)来注册组件,那么这个组件只允许在AutofacWebRequest域内或其子域内被解析。

我们的异常就发生在解析SingletonResolvable对象的时候。之前我们把它注册为全局单例(SingleInstance),所以它就处于根域内,而根域(正如名字所表达的)是所有其他域的父域。对依赖的解析是不允许向下朝着子域方向查找的,只允许向上照着其父域去查找依赖。综上所述,SingletonResolvable对象不可以去