Autofac笔记-LifeTimeScope(控制作用域和服务生命周期)


为什么不推荐从容器解析服务?
容器本身也是一个生命周期,你可以从容器解析服务,但是不推荐,因为容器在应用的生命周期内一直存在. 如果你直接从该容器中解析了太多东西, 应用结束时将会有一堆东西等着被释放,很有可能造成内存泄漏
因此应该从容器创建一个子生命周期,并从中解析,当解析完后释放子生命周期,解析的服务也一并清理了

服务的生命周期:
服务的生命周期是服务实例在程序中生存的时间。例如您“新建”一个实现IDisposable的对象,然后再对其进行调用Dispose()。

服务的范围:
服务范围是应用程序中可以与其他使用该服务的组件共享该服务的区域。例如,在你的应用程序中你可能有一个全局静态的单例 - 全局对象实例的“范围”将是整个应用程序。

Lifetime Scope(生命周期范围):

Autofac中生命周期范围的概念结合了这两个概念。实际上,生命周期范围等同于应用程序中的一个工作单元。一个工作单元可以在开始时开始一个生命周期范围,然后该工作单元所需的服务将从一个生命周期范围得到解析。当您解析服务时,Autofac跟踪已解析的一次性(可配置)组件。在工作单元结束时,您将释放相关的生命周期范围,Autofac将自动清理/释放解析后的服务。
务必始终从生命周期范围而不是根容器来解析服务。由于生命周期作用域的处置跟踪特性,如果您从容器(“根生命周期作用域”)中解析许多可丢弃组件,您可能会无意中给自己造成内存泄漏。根容器将一直保存对这些可丢弃组件的引用,直到其存在(通常是应用程序的生存期),因此它可以释放这些组件。

手动创建作用域:

您可以通过BeginLifetimeScope()从根容器开始在任何现有生存期范围上调用方法来创建生存期范围。生命周期范围是一次性的,并且它们跟踪组件的处置,因此请确保始终调用“ Dispose()”或将它们包装在“ using”语句中。

using(var scope = container.BeginLifetimeScope()) 
{ 
      //从容器的一个子作用域解析服务 
      var service = scope.Resolve();

      // 还可以创建嵌套的作用域
      using(var unitOfWorkScope = scope.BeginLifetimeScope()) 
      { 
            var anotherService = unitOfWorkScope.Resolve(); 
      } 
}

生命周期有以下几种:

瞬时(Per Dependency):每次请求都会创建新实例

builder.RegisterType().InstancePerDependency();

单例(Single Instance):所有对父容器或嵌套容器的请求都返回一个实例

builder.RegisterType().SingleInstance();

Per Lifetime Scope:

这个作用域适用于嵌套的生命周期。一个使用Per Lifetime 作用域的component在一个 nested lifetime scope内最多有一个实例。
当对象特定于一个工作单元时,这个非常有用。比如,一个HTTP请求,每一个工作单元都会创建一个nested lifetime,如果在每一次HTTP请求中创建一个nested lifetime,那么其他使用 per-lifetime 的component在每次HTTP请求中只会拥有一个实例。

builder.RegisterType().InstancePerLifetimeScope();

标记生命周期范围

在某些情况下,您希望跨工作单元共享服务,但您不希望像单例模式那样在全局共享这些服务。在这种情况下你可以使用InstancePerMatchingLifetimeScope来标识你的生命周期和服务。
举例如下,有个发邮件的组件,事务逻辑中需要发送多次邮件,所以可以在每个逻辑事务片中共享邮件服务。然后不希望邮件组件成为全局单例,可以如下设置。

//将事务级共享组件注册为InstancePerMatchingLifetimeScope,并给它一个“已知标记”,在启动新事务时使用。
var builder = new ContainerBuilder();
builder.RegisterType()
       .As()
       .InstancePerMatchingLifetimeScope("transaction");

// 订单处理器和收据管理器都需要发送电子邮件通知。
builder.RegisterType()
       .As();
builder.RegisterType()
       .As();

var container = builder.Build();
// 用标记创建作用域范围
using(var transactionScope = container.BeginLifetimeScope("transaction"))
{
  using(var orderScope = transactionScope.BeginLifetimeScope())
  {
      //IEmailSender将“驻留”在父事务作用域中,并且在作用域中的任何子作用域中共享
    var op = orderScope.Resolve();
    op.ProcessOrder();
  }
  using(var receiptScope = transactionScope.BeginLifetimeScope())
  {
    var rm = receiptScope.Resolve();
    rm.SendReceipt();
  }
}

添加注册到生命周期范围:

Autofac允许创建生命周期范围时“随时”添加注册。当您只需要一些不希望在全局范围内注册的附加东西时使用此方式注册

using(var scope = container.BeginLifetimeScope(
  builder =>
  {
    builder.RegisterType().As();
    builder.RegisterModule();
  }))
{
  // 额外的注册将只在这个生命周期范围内可用。
}

参考:
https://autofaccn.readthedocs.io/en/latest/lifetime/working-with-scopes.html
https://blog.csdn.net/WuLex/article/details/78704903