Autofac依赖注入简要说明(1)——Autofac中的生命周期

Autofac依赖注入简要说明(1)——Autofac中的生命周期

微软提供了官方的DI容器,有时候它并不能满足对应的需求,这时候就需要使用其他的DI容器,Autofac是.Net生态中一款老牌DI容器,功能齐全性能强劲

本文假设您已经对DI有一定的认识,如果不是很明白可以看看微软的官方文档依赖注入,本文只是讲一讲Autofac的一些常用方法

服务的生命周期

Autofac支持7种生命周期,分别为

Instance Per Dependency 瞬时

Instance Per Dependency和Microsoft DI容器中的Transient类似,它是一种瞬时的,每次调用都会生成新的实例,这种模式也是Autofac的默认模式,如果不特别指定,通过Autofac注册的服务都是每次获取都是新的实例

var builder = new ContainerBuilder();

// 注册Worker类为Instance Per Dependency(瞬时的),每次获取都会生成新的实例
builder.RegisterType<Worker>().InstancePerDependency();

// 如果不指定与InstancePerDependency相同,Instance Per Dependency是默认的模式
builder.RegisterType<Worker>();

Single Instance 单例

Single Instance和Microsoft DI容器中的Singleton类似,单例模式,所有的调用均返回同一个实例

var builder = new ContainerBuilder();
// 注册Worker类为SIngle Instance(单例),每次获取均返回同一个实例
builder.RegisterType<Worker>().SingleInstance();

Instance Per Lifetime Scope 作用域

Instance Per Lifetime Scope和Microsoft DI容器中的Scoped有一点类似,但是我并不确定是否完全相同,它在一个作用域中获取的实例相同,在Asp.Net Core中,每次客户端请求(连接)都是一个新的Scoped,所以Instance Per Lifetime Scope也是每次客户端请求(连接)都是一个新的实例,但是在当次请求中,请求注册的服务获得的是相同的实例

var builder = new ContainerBuilder();
// 注册Worker类为Instance Per Lifetime Scope(作用域)
// 在同一个作用域中获得的是相同实例,在不同作用域获得的是不同实例
builder.RegisterType<Worker>().InstancePerLifetimeScope();

using(var scope1 = container.BeginLifetimeScope())
{
     
  for(var i = 0; i < 100; i++)
  {
     
    // 在scope1中获取的Worker都是同一个实例
    var w1 = scope1.Resolve<Worker>();
  }
}

using(var scope2 = container.BeginLifetimeScope())
{
     
  for(var i = 0; i < 100; i++)
  {
     
    // 在scope2中获取的Worker都是同一个实例
    // 在scope2中获取的Worker实例和scope1中获取的Worker实例不相同,因为他们是两个不同的作用域
    var w2 = scope2.Resolve<Worker>();
  }
}

using(var scope3 = container.BeginLifetimeScope())
{
     
  var w3 = scope3.Resolve<Worker>();
  using(var scope4 = scope3.BeginLifetimeScope())
  {
     
    // w3和w4是不同的实例,因为他们是在不同的作用域中请求的
    var w4 = scope4.Resolve<Worker>();
  }
}

var w5 = container.Resolve<Worker>();
using(var scope5 = container.BeginLifetimeScope())
{
     
  // w5和w6不同
  // Scope是一个生命周期范围,如果从Scope中解析一个InstancePerLifetimeScope服务,
  // 该实例将在Scope的持续时间内存在,并且实际上是一个单例
  // 它将在容器的生命周期内被保存,以防止其他东西试图冲容器解析Worker
    
  // 解释:注册为InstancePerLifetimeScope的服务,在每个Scope中请求类似于请求单例,
  // 在这个单例的生命周期于Scope的生命周期相同,在Scope中请求对应实例则返回对应的单例,
  // 这样就避免冲突,每个Scope请求的都是自己的实例
  var w6 = scope5.Resolve<Worker>();
}

Instance Per Matching Lifetime Scope 匹配作用域

Instance Per Matching Lifetime Scope和Instance Per Lifetime Scope 类似,但是它支持对实例共享进行更精细的控制

当您创建一个嵌套的生命周期Scope时,您可以“标记”或“命名”该Scope。每个匹配标记的生命周期Scope作用域内最多只有一个与给定名称匹配的服务实例,包括嵌套的生命周期Scope。这允许您创建一种“作用域单例”,在这种单例中,其他嵌套的生命周期作用域可以共享组件的实例,而无需声明全局共享实例

这对于特定于单个工作单元的对象非常有用,例如一个HTTP请求,因为可以为每个工作单元创建一个嵌套的生命周期。如果为每个HTTP请求创建一个嵌套的Scope,那么任何具有生命周期Scope的组件都将为每个HTTP请求创建一个实例

在大多数应用程序中,只有一层容器嵌套就足以表示工作单元的范围。如果需要更多层次的嵌套(例如,像global->request->transaction),组件可以使用标记配置为在层次结构的特定层次上共享

var builder = new ContainerBuilder();
// 当您创建一个嵌套的生命周期Scope时,您可以“标记”或“命名”该范围。
// 具有每个匹配生命周期作用域的组件最多只有一个与给定名称匹配的嵌套生命周期作用域实例。
// 这允许您创建一种“作用域单例”,在这种单例中,其他嵌套的生命周期作用域可以共享组件的实例,而无需声明全局共享实例。
builder.RegisterType<Worker>().InstancePerMatchingLifetimeScope("my-request");

// 创建标记的作用域Scope
using(var scope1 = container.BeginLifetimeScope("my-request"))
{
     
  for(var i = 0; i < 100; i++)
  {
     
    var w1 = scope1.Resolve<Worker>();
    using(var scope2 = scope1.BeginLifetimeScope())
    {
     
      var w2 = scope2.Resolve<Worker>();

      // w1和w2的实例总是相同的,因为使用Instance Per Matching Lifetime Scope且为指定标记的Scope,
      // 嵌套的生命周期作用域可以共享实例,它实际上在标记作用域中是一个单例
    }
  }
}

// 创建另一个标记的作用域Scope
using(var scope3 = container.BeginLifetimeScope("my-request"))
{
     
  for(var i = 0; i < 100; i++)
  {
     
    // w3和w1/w2是不同的实例,因为他们是两个不同的生命周期,虽然它们的标记相同
    // Instance Per Matching Lifetime Scope依然是在不同的生命周期作用域创建新的实例
    var w3 = scope3.Resolve<Worker>();
    using(var scope4 = scope3.BeginLifetimeScope())
    {
     
      var w4 = scope4.Resolve<Worker>();

      // w3和w4是相同的实例,因为使用Instance Per Matching Lifetime Scope且为指定标记的Scope
      // 嵌套的生命周期作用域可以共享实例
      // w3和w4是同样的实例,w1和w2是同样的实例,但是w1/w2和w3/w4是不同的实例,因为他们是两个不同的作用域Scope
    }
  }
}

// 不能在标记不匹配的生命周期Scope中获取Instance Per Matching Lifetime Scope中标记的实例
// 会抛出异常
using(var noTagScope = container.BeginLifetimeScope())
{
     
   // 在没有正确命名(标记)的生命周期Scope的情况下试图解析每个匹配生命周期Scope的组件,会抛出异常
  var fail = noTagScope.Resolve<Worker>();
}

Instance Per Request

每次请求一个实例,在老的Asp.Net WebForm和Asp.Net MVC中使用,在Asp.net Core中使用的是 Instance Per Lifetime Scope,因此这里就不再介绍了,如果有需要可以自行查看官方文档

Instance Per Owned

这里的理解不是很好,仅供参考

Owned隐式关系类型创建了一个新的嵌套生命周期Scope。可以使用每个拥有的实例注册将依赖关系限定到拥有的实例,简单讲就是将依赖注入限定到对应泛型实例

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

using(var scope = container.BeginLifetimeScope())
{
     
  // MessageHandler依赖ServiceForHandler,它们的生命周期处于scope(当前Scope的名称)下的子生命周期范围内
  var h1 = scope.Resolve<Owned<MessageHandler>>();
  // 但是Instance Per Owned的实例需要自己处理,所以这里需要手动释放
  h1.Dispose();
}

Thread Scope

Thread Scope就很直观,代表每个线程的一个实例,对于多线程场景,必须非常小心,不要在派生线程下释放父作用域。如果您生成了线程,然后释放了父作用域,那么可能会陷入一个糟糕的情况,即无法解析组件。

var builder = new ContainerBuilder();
builder.RegisterType<MyThreadScopedComponent>()
       .InstancePerLifetimeScope();
var container = builder.Build();

void ThreadStart()
{
     
  using (var threadLifetime = container.BeginLifetimeScope())
  {
     
    var thisThreadsInstance = threadLifetime.Resolve<MyThreadScopedComponent>();
  }
}

你可能感兴趣的:(C#学习笔记,asp.net,c#,autofac,.net)