实例范围决定了如何在同一服务的请求之间共享实例。 请注意,您应该熟悉生命周期范围的概念,以更好地理解这里发生的事情。
当对一个服务进行请求时,Autofac可以返回一个实例(单个实例作用域),一个新实例(每个依赖作用域)或单个实例带某种上下文。 一个线程或一个HTTP请求(每个生命周期范围)。
这适用于从显式Resolve()调用返回的实例以及容器内部创建的实例,以满足另一个组件的依赖关系。
注意
选择正确的生命周期范围将有助于避免组件寿命过长或不够长的俘获依赖和其他陷阱。 开发人员需要为每个应用程序组件做出正确的选择。
Instance Per Dependency
Single Instance
Instance Per Lifetime Scope
Instance Per Matching Lifetime Scope
Instance Per Request
Instance Per Owned
Thread Scope
每个依赖的实例
在其他容器中也称为‘transient’或‘factory’。 使用每个依赖范围,每个服务请求将返回一个唯一的实例。
如果没有指定其他选项,则这是默认值。
var builder = new ContainerBuilder();
// 这...
builder.RegisterType<Worker>();
// ...一样:
builder.RegisterType<Worker>().InstancePerDependency();
当您解析每个依赖项的实例组件时,每次都会得到一个新的组件。
using(var scope = container.BeginLifetimeScope())
{
for(var i = 0; i < 100; i++)
{
//在这个循环中解析的100个工作者实例中的每一个都将是全新的。
var w = scope.Resolve<Worker>();
w.DoWork();
}
}
单一实例
这也被称为“singleton”。使用单个实例作用域,从根中的所有请求和所有嵌套作用域返回一个实例。
var builder = new ContainerBuilder();
builder.RegisterType<Worker>().SingleInstance();
当你解析一个单一的实例组件时,无论你在哪里请求它,总能得到相同的实例。
// 直接从容器中解析东西通常不是很好,但是对于单例演示的目的,就做了...
var root = container.Resolve<Worker>();
//我们可以从任何嵌套的生命周期作用域的任何级别解析工作者的任何次数。
using(var scope1 = container.BeginLifetimeScope())
{
for(var i = 0; i < 100; i++)
{
var w1 = scope1.Resolve<Worker>();
using(var scope2 = scope1.BeginLifetimeScope())
{
var w2 = scope2.Resolve<Worker>();
//root,w1和w2总是字面上相同的对象实例。 从哪个生命周期作用域解析或多少次都没有关系。
}
}
}
每个生命周期范围实例
这个范围适用于嵌套的生命周期。 具有每个生命周期范围的组件最多只有一个实例(每个嵌套生命周期范围)。
这对于特定于可能需要嵌套其他逻辑工作单元的单个工作单元的对象很有用。 每个嵌套的生命周期范围将获得注册的依赖关系的新实例。
var builder = new ContainerBuilder();
builder.RegisterType<Worker>().InstancePerLifetimeScope();
在解析每生命周期实例作用域组件时,每个嵌套作用域将获得一个实例(例如,每个工作单元)。
using(var scope1 = container.BeginLifetimeScope())
{
for(var i = 0; i < 100; i++)
{
//每次你在这个范围内解析这个问题,你都会得到同样的实例。
var w1 = scope1.Resolve<Worker>();
}
}
using(var scope2 = container.BeginLifetimeScope())
{
for(var i = 0; i < 100; i++)
{
//每次你在这个范围内解析这个问题,你都会得到同样的实例,但是这个实例不同于上面的范例中使用的实例。新范围=新实例(New scope = new instance).
var w2 = scope2.Resolve<Worker>();
}
}
每个匹配生命周期范围实例
这与上面的“实例每个生命周期范围”概念类似,但是可以更精确地控制实例共享。
当您创建一个嵌套的生存期范围时,您可以“标记”或“命名”范围。具有每匹配生命周期范围的组件至多有一个匹配给定名称的实例(每个嵌套生命周期范围)。这允许您创建一种“范围单例”,其中嵌套的生命周期范围可以在不声明全局共享实例的情况下共享组件的实例。
这对于特定于单个工作单元的对象是有用的,例如,一个HTTP请求,作为一个嵌套的生命周期可以创建每个工作单元。如果每个HTTP请求都创建一个嵌套的生命周期,那么每个具有每个生命周期作用域的组件都将为每个HTTP请求创建一个实例。 (有关每个请求生命周期范围的更多信息。)
在大多数应用中,只有一层容器嵌套足以代表工作单元的范围。如果需要更多级别的嵌套(例如像global-> request-> transaction这样的东西),组件可以被配置为使用标签在层次中的特定级别共享。
var builder = new ContainerBuilder();
builder.RegisterType<Worker>().InstancePerMatchingLifetimeScope("myrequest");
提供的标签值在启动时与生命周期范围相关联。 如果你尝试在没有正确命名的生命周期作用域时解析每个匹配生命周期作用域组件,则会得到一个异常。
//使用标签创建生命周期范围。
using(var scope1 = container.BeginLifetimeScope("myrequest"))
{
for(var i = 0; i < 100; i++)
{
var w1 = scope1.Resolve<Worker>();
using(var scope2 = scope1.BeginLifetimeScope())
{
var w2 = scope2.Resolve<Worker>();
//w1和w2始终是相同的对象实例,因为组件是每个匹配的生命周期范围,所以它实际上是在命名范围内的单例。
}
}
}
//使用标签创建另一个生命周期范围
using(var scope3 = container.BeginLifetimeScope("myrequest"))
{
for(var i = 0; i < 100; i++)
{
//w3将比早先标记的生命期范围内解决的工作者有所不同
var w3 = scope3.Resolve<Worker>();
using(var scope4 = scope3.BeginLifetimeScope())
{
var w4 = scope4.Resolve<Worker>();
//w3和w4始终是同一个对象,因为它们处于相同的标记范围内,但它们与先前的工作者(w1,w2)不一样。
}
}
}
//如果没有匹配的作用域,则无法解析每个匹配生命周期的组件。
using(var noTagScope = container.BeginLifetimeScope())
{
//这会抛出一个异常,因为这个范围没有预期的标记,也没有任何父范围!
var fail = noTagScope.Resolve<Worker>();
}
每个请求实例
某些应用程序类型自然适用于“请求”类型的语义,例如ASP.NET web forms和MVC应用程序。 在这些应用程序类型中,有能力拥有一种“每个请求的单例”。
每个请求的实例通过提供一个众所周知的生命周期范围标记,一个注册便捷方法以及针对常见应用程序类型的集成,在每个匹配的生命周期范围上建立在实例之上。 但是在幕后,每个匹配的生命周期范围仍然只是实例。
这意味着,如果您尝试解析已注册为“每个请求实例”的组件,但目前没有请求,则会收到异常。
有一个详细的常见问题,概述如何处理每个请求的生命周期。
var builder = new ContainerBuilder();
builder.RegisterType<Worker>().InstancePerRequest();
ASP.NET Core使用实例每生命周期范围而不是每个实例每个请求。 有关更多信息,请参阅ASP.NET Core集成文档。
Instance Per Owned
The Owned implicit relationship type creates new nested lifetime scopes. It is possible to scope dependencies to the owned instance using the instance-per-owned registrations.
每个Owned实例
Owned<T>隐式关系类型创建新的嵌套生命周期作用域。 可以使用每个Owned实例注册来将依赖关系的范围限定在所拥有的实例中。
var builder = new ContainerBuilder();
builder.RegisterType<MessageHandler>();
builder.RegisterType<ServiceForHandler>().InstancePerOwned<MessageHandler>();
在这个例子中,ServiceForHandler服务将被限制到拥有的MessageHandler实例的生命周期。
using(var scope = container.BeginLifetimeScope())
{
//消息处理程序本身以及已解析的相关ServiceForHandler服务位于“scope”下的一个小小的生命周期范围内。 请注意,解析Owned<T>意味着您有责任处置。
var h1 = scope.Resolve<Owned<MessageHandler>>();
h1.Dispose();
}
线程范围
Autofac可以强制绑定到一个线程的对象不会满足(绑定到另一个线程的组件的)依赖关系。 虽然没有一个方便的方法,你可以使用生命周期范围。
var builder = new ContainerBuilder();
builder.RegisterType<MyThreadScopedComponent>()
.InstancePerLifetimeScope();
var container = builder.Build();
然后,每个线程都有自己的生命周期范围:
void ThreadStart()
{
using (var threadLifetime = container.BeginLifetimeScope())
{
var thisThreadsInstance = threadLifetime.Resolve<MyThreadScopedComponent>();
}
}
重要提示:鉴于多线程场景,您必须非常小心,父级作用域不会从产生的线程下退出。
如果您生成线程,然后处置父范围,则可能会遇到组件无法解析的情况。
每个通过ThreadStart()执行的线程都将获得自己的MyThreadScopedComponent实例 - 这在本质上是在生命周期范围内的“单例”。 由于有限范围的实例从不提供给外部范围,所以更容易分离线程组件。
您可以通过使用ILifetimeScope参数将父生存期范围注入生成该线程的代码中。 Autofac知道自动注入当前的生命期范围,你可以从中创建一个嵌套的范围。
public class ThreadCreator
{
private ILifetimeScope _parentScope;
public ThreadCreator(ILifetimeScope parentScope)
{
this._parentScope = parentScope;
}
public void ThreadStart()
{
using (var threadLifetime = this._parentScope.BeginLifetimeScope())
{
var thisThreadsInstance = threadLifetime.Resolve<MyThreadScopedComponent>();
}
}
}
如果你想更强制执行这个操作,可以使用每个匹配的生命周期作用域的实例(参见上面)将线程作用域的组件与内部生命周期相关联(它们仍然依赖于注入的外部容器中的factory/singleton组件) 。这种方法的结果如下所示:
图中的‘contexts’ 是使用BeginLifetimeScope()创建的容器。
818

被折叠的 条评论
为什么被折叠?



