猿教程 Logo

Autofac教程:实例范围

实例范围确定在相同服务的请求之间如何共享实例。 请注意,您应该熟悉终生范围的概念,以更好地了解此处发生的情况。

当对服务进行请求时,Autofac可以返回单个实例(单实例范围),新实例(每个依赖范围)或某种类型上下文中的单个实例,例如, 线程或HTTP请求(每个生命周期范围)。

这适用于从显式Resolve()调用返回的实例以及容器内部创建的实例,以满足另一个组件的依赖关系。

选择合适的生存期范围将帮助您避免在组件寿命过长或不够长的情况下捕获依赖关系和其他陷阱。开发人员可以为每个应用程序组件做出正确的选择。


依赖实例

在其他容器中也称为“瞬态”或“工厂”。 使用每个依赖关系范围,将从每个服务请求中返回唯一的实例。

如果没有指定其他选项,则为默认值。

var builder = new ContainerBuilder();

// This...
builder.RegisterType<Worker>();

// ...is the same as this:
builder.RegisterType<Worker>().InstancePerDependency();

当您解析每个依赖关系的实例的组件时,每次都会获得一个新的组件。

using(var scope = container.BeginLifetimeScope())
{
  for(var i = 0; i < 100; i++)
  {
    // Every one of the 100 Worker instances
    // resolved in this loop will be brand new.
    var w = scope.Resolve<Worker>();
    w.DoWork();
  }
}

单实例

这也被称为“单例”。使用单个实例范围,从根和所有嵌套作用域中的所有请求返回一个实例。

var builder = new ContainerBuilder();
builder.RegisterType<Worker>().SingleInstance();

当您解析单个实例组件时,无论您在何处请求,都始终可以获得相同的实例。

// It's generally not good to resolve things from the
// container directly, but for singleton demo purposes
// we do...
var root = container.Resolve<Worker>();

// We can resolve the worker from any level of nested
// lifetime scope, any number of times.
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, and w2 are always literally the
      // same object instance. It doesn't matter
      // which lifetime scope it's resolved from
      // or how many times.
    }
  }
}

每个终身范围的实例

此范围适用于嵌套寿命。 每个生命周期范围的组件每个嵌套生命周期范围最多只能有一个实例。

这对于特定于可能需要嵌套其他逻辑工作单元的单个工作单元的对象很有用。 每个嵌套生命周期范围将获得注册依赖关系的新实例。

var builder = new ContainerBuilder();
builder.RegisterType<Worker>().InstancePerLifetimeScope();

当您为每个生命周期范围组件解析实例时,每个嵌套作用域(例如,每单位工作量)获得单个实例。

using(var scope1 = container.BeginLifetimeScope())
{
  for(var i = 0; i < 100; i++)
  {
    // Every time you resolve this from within this
    // scope you'll get the same instance.
    var w1 = scope1.Resolve<Worker>();
  }
}

using(var scope2 = container.BeginLifetimeScope())
{
  for(var i = 0; i < 100; i++)
  {
    // Every time you resolve this from within this
    // scope you'll get the same instance, but this
    // instance is DIFFERENT than the one that was
    // used in the above scope. New scope = new instance.
    var w2 = scope2.Resolve<Worker>();
  }
}

每个匹配生命周期范围的实例

这与上面的“每个生命周期的实例”概念相似,但允许对实例共享进行更精确的控制。

创建嵌套生命周期范围时,您可以“标记”或“命名”作用域。 每个匹配生命周期范围的组件最多只能具有匹配给定名称的每个嵌套生命周期范围的单个实例。 这允许您创建一种“范围单例”,其他嵌套生命周期范围可以共享组件的实例而不声明全局共享实例。

这对于单个工作单元特定的对象是有用的,例如 作为嵌套生命周期的HTTP请求可以在每个工作单元中创建。 如果每个HTTP请求创建一个嵌套生命周期,那么具有每个生命周期范围的任何组件将具有每HTTP请求的实例。 (有关每个请求生命周期的更多信息)。

在大多数应用中,只有一个级别的容器嵌套将足以表示工作单元的范围。 如果需要更多层次的嵌套(例如像全局 - >请求 - >事务),组件可以配置为使用标签在层次结构中的特定级别共享。

var builder = new ContainerBuilder();
builder.RegisterType<Worker>().InstancePerMatchingLifetimeScope("myrequest");

提供的标签值在启动时与寿命范围相关联。 如果在没有正确命名的生命周期范围的情况下尝试解析每个匹配的生命周期范围组件,那么您将收到异常。

// Create the lifetime scope using the tag.
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 and w2 are always the same object
      // instance because the component is per-matching-lifetime-scope,
      // so it's effectively a singleton within the
      // named scope.
    }
  }
}

// Create another lifetime scope using the tag.
using(var scope3 = container.BeginLifetimeScope("myrequest"))
{
  for(var i = 0; i < 100; i++)
  {
    // w3 will be DIFFERENT than the worker resolved in the
    // earlier tagged lifetime scope.
    var w3 = scope3.Resolve<Worker>();
    using(var scope4 = scope3.BeginLifetimeScope())
    {
      var w4 = scope4.Resolve<Worker>();

      // w3 and w4 are always the same object because
      // they're in the same tagged scope, but they are
      // NOT the same as the earlier workers (w1, w2).
    }
  }
}

// You can't resolve a per-matching-lifetime-scope component
// if there's no matching scope.
using(var noTagScope = container.BeginLifetimeScope())
{
  // This throws an exception because this scope doesn't
  // have the expected tag and neither does any parent scope!
  var fail = noTagScope.Resolve<Worker>();
}

每个请求的实例

一些应用程序类型自然就适用于“请求”类型语义,例如ASP.NET Web窗体和MVC应用程序。 在这些应用程序类型中,有能力拥有一种“每个请求的单例”。

每个请求的实例通过提供公知的生命周期范围标签,注册便利方法和常见应用程序类型的集成,在每个匹配的生命周期范围之上构建实例。 但是,在幕后,它仍然是每个匹配的生命周期范围的实例。

这意味着,如果您尝试解析注册为每个请求实例的组件,但没有当前的请求,您将得到一个异常。

有一个详细的FAQ,概述如何处理每个请求的生命周期。

var builder = new ContainerBuilder();
builder.RegisterType<Worker>().InstancePerRequest();

ASP.NET Core使用每个终身职位范围而不是每个请求的实例。 有关更多信息,请参阅ASP.NET Core集成文档。


每个实例

所有者隐式关系类型创建新的嵌套生命周期范围。 可以使用每个实例的注册范围对所有实例的依赖关系。

var builder = new ContainerBuilder();
builder.RegisterType<MessageHandler>();
builder.RegisterType<ServiceForHandler>().InstancePerOwned<MessageHandler>();

在此示例中,ServiceForHandler服务将被限定为拥有的MessageHandler实例的生命周期。

using(var scope = container.BeginLifetimeScope())
{
  // The message handler itself as well as the
  // resolved dependent ServiceForHandler service
  // is in a tiny child lifetime scope under
  // "scope." Note that resolving an Owned<T>
  // means YOU are responsible for disposal.
  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>();
    }
  }
}

如果您希望更加强制执行此操作,请在每个匹配的使用寿命范围内使用实例(见上文)将线程范围的组件与内部生命周期相关联(它们仍然具有从外部容器注入的工厂/单例组件的依赖关系 。)这种方法的结果看起来像:


图中的“上下文”是使用BeginLifetimeScope()创建的容器。


版权声明:本站所有教程均为本站原创或翻译,转载请注明出处,请尊重他人劳动果实。请记住本站地址:www.yuanjiaocheng.net (猿教程) 作者:卿文刚
本文标题: C#环境
本文地址:http://www.yuanjiaocheng.net/Autofac/instance-scope.html