查漏补缺之Autofac生命周期

好的,Autofac 的生命周期(Lifetime)是其核心概念之一,它决定了对象的实例何时被创建、如何共享以及何时被销毁。正确理解和使用生命周期是避免内存泄漏和确保程序行为正确的关键。

Autofac 主要提供了以下几种生命周期作用域(Lifetime Scopes):


1. Instance Per Dependency (每次依赖一个新实例)

  • 行为 : 每次请求该服务时都会创建一个新的、独立的实例。这是最基础的、默认的生命周期(如果未显式指定的话)。

  • 类比 : 与 ASP.NET Core 内置 DI 容器中的 Transient 生命周期类似。

  • 使用场景: 适用于无状态的、轻量级的服务,或者那些不需要在多次调用之间共享状态的服务。

  • 注册方式

    csharp 复制代码
    builder.RegisterType<MyService>().As<IMyService>().InstancePerDependency();
    // 或者省略,因为这是默认行为
    builder.RegisterType<MyService>().As<IMyService>();

2. Single Instance (单例)

  • 行为 : 在整个容器的生命周期内,只有一个实例被创建。所有对该服务的请求都会返回这同一个实例。该实例由容器持有,直到容器本身被释放。

  • 类比 : 与 ASP.NET Core 内置 DI 容器中的 Singleton 生命周期类似。

  • 使用场景: 适用于全局共享的、无状态的或线程安全的服务,例如配置对象、缓存管理器、日志记录器(某些实现)、数据库连接池等。

  • 注意: 使用此范围需非常小心,避免意外共享可变状态导致线程安全问题。

  • 注册方式

    csharp 复制代码
    builder.RegisterType<MySingletonService>().As<IMySingletonService>().SingleInstance();

3. Instance Per Lifetime Scope (每个作用域一个实例)

  • 行为在一个生命周期作用域(Lifetime Scope)内是单例的。同一个作用域内的多次请求会返回同一个实例。但在不同的作用域中,会创建不同的实例。

  • 类比 : 与 ASP.NET Core 内置 DI 容器中的 Scoped 生命周期完全相同

  • 使用场景 : 这是 Web 应用程序中最常用的范围。它完美匹配了"每次 HTTP 请求"的概念。例如,数据库上下文(DbContext)、仓储(Repository)、业务逻辑服务等,通常被注册为此范围,以确保在一次请求内共享同一个实例,处理完请求后自动释放。

  • 注册方式

    csharp 复制代码
    builder.RegisterType<MyScopedService>().As<IMyScopedService>().InstancePerLifetimeScope();

4. Instance Per Request (每个请求一个实例) - Web 专用

  • 行为 : 这是 Instance Per Lifetime ScopeWeb 应用程序中的一个特化别名。它专门表示"每个 HTTP 请求一个实例"。

  • 注意 : 此范围仅在 Web 应用程序(如 ASP.NET MVC, Web API)的上下文中有效。如果你在非 Web 环境中使用它,会抛出异常。

  • 使用场景 : 与 InstancePerLifetimeScope 完全相同,但语义上更清晰地表明了其用于 Web 请求。

  • 注册方式

    csharp 复制代码
    builder.RegisterType<MyDbContext>().As<IMyDbContext>().InstancePerRequest();
    // 在现代 ASP.NET Core 中,通常更推荐使用 `InstancePerLifetimeScope`,
    // 因为 `InstancePerRequest` 是建立在 per-scope 机制之上的。

5. Instance Per Matching Lifetime Scope (每个匹配的作用域一个实例)

  • 行为 : 这是一种更高级的用法。它允许你创建"标签化"的作用域。只有在父作用域具有匹配的标签时,服务才会共享同一个实例。

  • 使用场景 : 用于创建具有特定含义的、嵌套的子系统或工作单元。例如,你可以创建一个名为 "unit-of-work" 的标签作用域,所有在该作用域内注册为 InstancePerMatchingLifetimeScope("unit-of-work") 的服务都会共享同一个实例。

  • 示例

    csharp 复制代码
    // 注册服务时指定标签
    builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerMatchingLifetimeScope("unit-of-work");
    
    // 在代码中创建带标签的作用域
    using (var unitOfWorkScope = container.BeginLifetimeScope("unit-of-work"))
    {
        var uow1 = unitOfWorkScope.Resolve<IUnitOfWork>();
        var uow2 = unitOfWorkScope.Resolve<IUnitOfWork>();
        // uow1 和 uow2 是同一个实例
    
        // 你可以在这个作用域内解析其他依赖,如果它们依赖于 IUnitOfWork,
        // 得到的将是上面这个共享的实例。
    }

6. Instance Per Owned (每个 owned 实例一个实例)

  • 行为 : 与 Owned<T> 关系类型配合使用。Owned<T> 表示一种隐式的关系作用域。每次解析一个 Owned<T> 时,你实际上得到了一个它自己的小型生命周期作用域,该服务在这个小型作用域内是单例的。

  • 使用场景 : 用于需要精细控制某个特定依赖及其子依赖生命周期的场景,尤其是当该服务实现了 IDisposable 时,你可以通过释放 Owned<T> 来精确地释放该服务及其依赖链。

  • 示例

    csharp 复制代码
    builder.RegisterType<ExpensiveResource>().As<IExpensiveResource>().InstancePerOwned();
    
    // 使用
    using (var ownedResource = scope.Resolve<Owned<IExpensiveResource>>())
    {
        var resource = ownedResource.Value; // 获取真正的服务实例
        resource.DoSomething();
    } // using 块结束时,ownedResource 及其内部的 IExpensiveResource 会被自动释放。

总结与对比

生命周期范围 描述 类比 ASP.NET Core DI 常用场景
Instance Per Dependency 每次请求都创建新实例 Transient 无状态、轻量级服务
Single Instance 全局单例 Singleton 配置、缓存、日志器(线程安全)
Instance Per Lifetime Scope 每个作用域内单例 Scoped Web 请求、工作单元(最常用)
Instance Per Request 每个 HTTP 请求单例(Web 专用) Scoped Web 请求(同上,是上者的特化)
Instance Per Matching Lifetime Scope 每个匹配的标签作用域内单例 无直接对应 嵌套的、有特定含义的子系统
Instance Per Owned 每个 Owned<T> 实例内单例 无直接对应 需要精细控制生命周期的昂贵资源

核心建议

  1. 在 Web 开发中,InstancePerLifetimeScope 是你的首选,用于大多数服务。
  2. 对于真正的全局共享且无状态的资源,使用 SingleInstance
  3. 对于轻量级、无状态的工具类,使用 InstancePerDependency(或默认)。
  4. 始终牢记作用域的概念,特别是在解析实现了 IDisposable 的对象时,确保它们在正确的作用域内被创建和释放。
相关推荐
软件黑马王子1 天前
C#练习题——泛型实现单例模式和增删改查
开发语言·单例模式·c#
wangyue41 天前
Doxygen with C#
c#
爱吃小胖橘1 天前
Unity-动画基础
unity·c#·游戏引擎
arbboter1 天前
【代码】关于C#支持文件和文本框的简单日志实现
数据库·c#·日志·log·日志库
Eiceblue1 天前
使用 C# 操作 Excel 工作表:添加、删除、复制、移动、重命名
服务器·开发语言·c#·excel
娶不到胡一菲的汪大东1 天前
C#第五讲 函数的用法
开发语言·c#
coding-fun1 天前
SuperScript:C#脚本编辑器、C#脚本引擎
开发语言·c#·编辑器
dephixf1 天前
C#开发一个WinCC浏览器组件,WinCC脚本调用直接打开Web应用
c#·mom·scada·wincc·wincc浏览器
一个帅气昵称啊1 天前
在.NET中实现RabbitMQ客户端的优雅生命周期管理及二次封装
分布式·后端·架构·c#·rabbitmq·.net
ellis19701 天前
toLua[二] Examples 01_HelloWorld分析
unity·c#·lua