好的,Autofac 的生命周期(Lifetime)是其核心概念之一,它决定了对象的实例何时被创建、如何共享以及何时被销毁。正确理解和使用生命周期是避免内存泄漏和确保程序行为正确的关键。
Autofac 主要提供了以下几种生命周期作用域(Lifetime Scopes):
1. Instance Per Dependency (每次依赖一个新实例)
-
行为 : 每次请求该服务时都会创建一个新的、独立的实例。这是最基础的、默认的生命周期(如果未显式指定的话)。
-
类比 : 与 ASP.NET Core 内置 DI 容器中的
Transient
生命周期类似。 -
使用场景: 适用于无状态的、轻量级的服务,或者那些不需要在多次调用之间共享状态的服务。
-
注册方式 :
csharpbuilder.RegisterType<MyService>().As<IMyService>().InstancePerDependency(); // 或者省略,因为这是默认行为 builder.RegisterType<MyService>().As<IMyService>();
2. Single Instance (单例)
-
行为 : 在整个容器的生命周期内,只有一个实例被创建。所有对该服务的请求都会返回这同一个实例。该实例由容器持有,直到容器本身被释放。
-
类比 : 与 ASP.NET Core 内置 DI 容器中的
Singleton
生命周期类似。 -
使用场景: 适用于全局共享的、无状态的或线程安全的服务,例如配置对象、缓存管理器、日志记录器(某些实现)、数据库连接池等。
-
注意: 使用此范围需非常小心,避免意外共享可变状态导致线程安全问题。
-
注册方式 :
csharpbuilder.RegisterType<MySingletonService>().As<IMySingletonService>().SingleInstance();
3. Instance Per Lifetime Scope (每个作用域一个实例)
-
行为 : 在一个生命周期作用域(Lifetime Scope)内是单例的。同一个作用域内的多次请求会返回同一个实例。但在不同的作用域中,会创建不同的实例。
-
类比 : 与 ASP.NET Core 内置 DI 容器中的
Scoped
生命周期完全相同。 -
使用场景 : 这是 Web 应用程序中最常用的范围。它完美匹配了"每次 HTTP 请求"的概念。例如,数据库上下文(DbContext)、仓储(Repository)、业务逻辑服务等,通常被注册为此范围,以确保在一次请求内共享同一个实例,处理完请求后自动释放。
-
注册方式 :
csharpbuilder.RegisterType<MyScopedService>().As<IMyScopedService>().InstancePerLifetimeScope();
4. Instance Per Request (每个请求一个实例) - Web 专用
-
行为 : 这是
Instance Per Lifetime Scope
在 Web 应用程序中的一个特化别名。它专门表示"每个 HTTP 请求一个实例"。 -
注意 : 此范围仅在 Web 应用程序(如 ASP.NET MVC, Web API)的上下文中有效。如果你在非 Web 环境中使用它,会抛出异常。
-
使用场景 : 与
InstancePerLifetimeScope
完全相同,但语义上更清晰地表明了其用于 Web 请求。 -
注册方式 :
csharpbuilder.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>
来精确地释放该服务及其依赖链。 -
示例 :
csharpbuilder.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> 实例内单例 |
无直接对应 | 需要精细控制生命周期的昂贵资源 |
核心建议:
- 在 Web 开发中,
InstancePerLifetimeScope
是你的首选,用于大多数服务。 - 对于真正的全局共享且无状态的资源,使用
SingleInstance
。 - 对于轻量级、无状态的工具类,使用
InstancePerDependency
(或默认)。 - 始终牢记作用域的概念,特别是在解析实现了
IDisposable
的对象时,确保它们在正确的作用域内被创建和释放。