一、依赖注入(DI)服务生命周期

.NET 8延续了三种经典生命周期模型,但通过底层优化提升稳定性和性能:

  1. Transient(瞬时)

    • 特点:每次请求都创建新实例。

    • 适用场景:无状态服务(如工具类、计算逻辑)13。

    • 注意:高频请求下可能引发GC压力,需评估对象创建开销。

  2. Scoped(作用域)

    • 特点:在同一作用域(如HTTP请求)内复用实例,跨作用域隔离。

    • 适用场景:DbContext、用户会话管理等35。

    • .NET 8增强

      • 在Blazor全栈渲染中,自动关联组件作用域,避免状态污染38。

      • 异步作用域支持(AsyncServiceScope),优化异步资源清理。

  3. Singleton(单例)

    • 特点:全局唯一实例,应用启动时创建。

    • 适用场景:配置中心、缓存服务14。

    • 风险:多线程竞争需显式同步(如 lockConcurrentDictionary)。

// 注册示例
builder.Services.AddTransient<IToolService, ToolService>();  
builder.Services.AddScoped<IUserSession, UserSession>();  
builder.Services.AddSingleton<IConfigCache, ConfigCache>();

二、三种依赖注入生命周期对比

类型

实例创建时机

作用域范围

适用场景

注意事项

Transient

每次请求

独立于所有作用域

无状态服务、工具类

高频请求时GC压力大

Scoped

作用域内首次请求

同一作用域内共享

DbContext、用户会话

跨作用域访问会引发异常

Singleton

应用启动时

全局共享

配置服务、缓存

需线程同步,避免竞争条件

三、细谈scoped(作用域)

1.作用域的本质:逻辑隔离边界。

作用域是一个逻辑容器,它:

a.创建边界:为每个独立操作单元(如HTTP请求)创建隔离的DI容器
b.管理实例:在边界内保持服务实例的唯一性
c.自动清理:边界结束时自动释放实现了IDisposable的资源
// 作用域的手动创建示例
using (var scope = serviceProvider.CreateScope())
{
    var scopedService = scope.ServiceProvider.GetService<IMyScopedService>();
    scopedService.DoWork();
} // 作用域结束时自动释放资源

2、ASP.NET Core中的典型作用域:HTTP请求生命周期

在Web应用中,作用域与HTTP请求强绑定:

696a44f88920d.png

关键特性:

a. 请求级隔离
  • 每个请求获取独立的DbContext实例

  • 并发请求不会共享状态

// Startup.cs中的典型配置
services.AddDbContext<AppDbContext>(); // 默认Scoped生命周期
b. 组件级共享
  • 在同一个请求中,多次解析获得相同实例

// 在同一个HTTP请求中:
var service1 = HttpContext.RequestServices.GetService<IService>();
var service2 = HttpContext.RequestServices.GetService<IService>();
// service1 == service2 返回 true
c. 自动资源释放
  • 请求结束时自动调用Dispose()

  • 避免数据库连接泄露

public class AppDbContext : DbContext 
{
    // 请求结束时自动关闭连接
    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlServer(_configuration.GetConnectionString("Default"));
}

3. 作用域的底层实现机制

a. 容器层级结构:
696a44f88920d.png
b. 关键接口解析
public interface IServiceScope : IDisposable
{
    IServiceProvider ServiceProvider { get; }
}
​
public interface IServiceScopeFactory
{
    IServiceScope CreateScope();
}

4. Scoped服务的特殊注意事项

a. 禁止跨作用域传递
// 错误示例:将Scoped服务保存到Singleton
public class SingletonService
{
    private readonly IScopedService _scopedService;
    
    // 反模式!会导致作用域泄露
    public SingletonService(IScopedService scopedService) 
        => _scopedService = scopedService;
}
b. 正确解决方案
public class SingletonService
{
    private readonly IServiceScopeFactory _scopeFactory;
    
    public SingletonService(IServiceScopeFactory scopeFactory)
        => _scopeFactory = scopeFactory;
    
    public void Method()
    {
        using var scope = _scopeFactory.CreateScope();
        var scopedService = scope.ServiceProvider
            .GetRequiredService<IScopedService>();
        // 安全使用
    }
}

c. 异步作用域支持(.NET 8增强)

public async Task ProcessAsync()
{
    await using var scope = _scopeFactory.CreateAsyncScope();
    var service = scope.ServiceProvider.GetService<IAsyncService>();
    await service.DoWorkAsync();
} // 自动异步清理资源