在 ASP.NET Core 中,理解依赖注入(DI)和如何注册服务是至关重要的。
依赖注入的三种常见注册方式分别是:Singleton、Transient 和 Scoped。
注册方式决定了对象实例的生命周期,以及它们如何在整个应用程序中共享。
一个错误的选择,可能导致内存泄漏、数据混乱、性能下降……
接下来,我们将深入探讨这三种注册方式及适用场景。
Singleton(单例)
定义
Singleton 是一种服务注册方式,它保证在应用程序生命周期内只会创建一个服务实例,并且这个实例会在每次需要该服务时被重用。这意味着无论调用多少次,返回的始终是同一个实例。
适用场景
- o 对于那些需要跨多个请求共享状态的服务,Singleton 是一个理想的选择。
- o 它适用于不会发生更改或维护全局状态的服务,比如日志记录、配置管理、缓存等。
代码
public class MySingletonService
{
public MySingletonService()
{
Console.WriteLine("MySingletonService 实例化");
}
public string GetMessage() => "我是一个单例服务";
}
services.AddSingleton<MySingletonService>();
每次从容器获取 MySingletonService 时,它都会返回同一个实例
注意事项:
- o Singleton 会持续存在于整个应用程序生命周期,直到应用程序结束。
- o 如果 Singleton 服务依赖其他服务,那么这些依赖服务的生命周期必须至少是 Singleton。
Transient(瞬态)
定义
Transient 是一种服务注册方式,它每次请求都会创建一个新的实例。这意味着每次从容器获取该服务时都会返回一个全新的对象。
适用场景
- o 当你需要一个短生命周期的服务,且该服务不持有任何状态时,可以使用 Transient。
- o 适合于轻量级的、无状态的服务,如数据处理或临时的操作服务。
###代码
services.AddTransient<MyTransientService>();
每次从容器获取 MyTransientService 时,它都会创建一个新的实例
注意事项:
- o Transient 服务的实例会在每次请求时被创建,因此它适合那些不需要持久化状态的服务。
- o 如果 Transient 服务依赖于其他服务,那么这些依赖服务的生命周期也应该尽量是 Transient。
Scoped(作用域)
定义
Scoped 是一种服务注册方式,它的实例会在一个请求或一个作用域(例如,一个用户请求的生命周期)内创建,并且在整个作用域内共享同一个实例。当作用域结束时,实例会被销毁。
适用场景
- o Scoped 适用于那些在请求期间共享状态的服务,比如数据库上下文等。通常在处理请求时,每个请求需要自己的服务实例,但是这些服务实例在整个请求中保持一致。
- o 它适用于那些需要在请求生命周期内保持状态,但不应跨请求共享状态的服务。
代码
services.AddScoped<MyScopedService>();
每个请求都会创建一个新的 MyScopedService 实例,但在同一个请求期间,它会始终保持一致:
注意事项:
- o Scoped 适合于与每个请求相关的服务,比如数据库连接等。
- o 如果 Scoped 服务依赖其他服务,那么这些依赖服务的生命周期应该至少是 Scoped。
选择
场景推荐生命周期全局缓存、配置读取Singleton每次调用都要独立计算或验证Transient数据库操作、请求上下文信息Scoped服务依赖其他 Scoped/TransientScoped服务依赖 Singleton可用任何服务被 Scoped 使用建议 Scoped 或 Transient
比较
生命周期注册方式服务实例化频率适用场景单例(Singleton)AddSingleton<T>()应用程序生命周期内唯一实例跨请求共享状态的服务瞬态(Transient)AddTransient<T>()每次请求都创建新的实例短生命周期、无状态的服务作用域(Scoped)AddScoped<T>()每个请求一个实例请求级别的状态共享服务