查漏补缺之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 的对象时,确保它们在正确的作用域内被创建和释放。
相关推荐
武藤一雄39 分钟前
彻底吃透.NET中序列化反序列化
xml·微软·c#·json·.net·.netcore
one9961 小时前
C# 的进程间通信(IPC,Inter-Process Communication)
开发语言·c#
CreasyChan1 小时前
unity-向量数学:由浅入深详解
unity·c#
专注VB编程开发20年11 小时前
C#全面超越JAVA,主要还是跨平台用的人少
java·c#·.net·跨平台
小猪快跑爱摄影14 小时前
【AutoCad 2025】【C#】零基础教程(四)——MText 常见属性
c#·autocad
炼钢厂16 小时前
C#6——DateTime
c#
Lv117700817 小时前
Visual Studio中的多态
ide·笔记·c#·visual studio
wuguan_17 小时前
C#:多态函数重载、态符号重载、抽象、虚方法
开发语言·c#
我不是程序猿儿18 小时前
【C#】ScottPlot的Refresh()
开发语言·c#
工程师00718 小时前
C# 基于 HSL 与基恩士 PLC 通信
c#·mc协议·基恩士plc