前置知识: C#

依赖注入生命周期

00:00
2 min Advanced 2026/6/14

ASP.NET Core依赖注入生命周期详解:Scoped、Transient、Singleton。

概述

ASP.NET Core 的依赖注入(DI)容器支持三种服务生命周期:Singleton、Scoped 和 Transient。正确选择生命周期是构建可维护性能应用的关键。错误的生命周期配置可能导致数据泄漏、并发 Bug 或性能问题。

基础概念

三种生命周期

// 单例:整个应用生命周期内只有一个实例
services.AddSingleton<IDatabase, Database>();

// 作用域:每个 HTTP 请求内共享一个实例
services.AddScoped<IUserService, UserService>();

// 瞬态:每次注入都创建新实例
services.AddTransient<ILogger, Logger>();

生命周期对比

生命周期创建时同一请求不同请求适用场景
Singleton首次请求同一实例同一实例配置缓存数据库连接
Scoped每次请求同一实例不同实例请求上下文、数据库上下文
Transient每次注入不同实例不同实例轻量级状态服务

快速上手

注册服务

// 在 Program.cs 中注册服务
var builder = WebApplication.CreateBuilder(args);

// 单例服务
builder.Services.AddSingleton<ICacheService, RedisCacheService>();
builder.Services.AddSingleton(new AppConfig { MaxRetries = 3 });

// 作用域服务
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddDbContext<AppDbContext>(); // EF Core 默认是 Scoped

// 瞬态服务
builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();

var app = builder.Build();

构造器注入

public class OrderController : ControllerBase {
    private readonly IOrderService _orderService;
    private readonly ICacheService _cacheService;
    private readonly ILogger<OrderController> _logger;

    // 构造器注入(推荐方式)
    public OrderController(
        IOrderService orderService,
        ICacheService cacheService,
        ILogger<OrderController> logger) {
        _orderService = orderService;
        _cacheService = cacheService;
        _logger = logger;
    }
}

详细用法

Captive Dependency 陷阱

// 错误:Singleton 依赖 Scoped 服务
// Singleton 服务只创建一次,其依赖的 Scoped 服务也只创建一次
// Scoped 服务被"囚禁"在 Singleton 中,变成单例行为
services.AddSingleton<ISingletonService, SingletonService>();
// SingletonService 构造器中注入了 Scoped 服务

// 依赖方向规则:
// Singleton 只能依赖 Singleton
// Scoped 可以依赖 Scoped 或 Singleton
// Transient 可以依赖任意生命周期

// 解决方案:使用 IServiceProvider 延迟解析
public class SingletonService {
    private readonly IServiceProvider _serviceProvider;

    public SingletonService(IServiceProvider serviceProvider) {
        _serviceProvider = serviceProvider;
    }

    public void DoWork() {
        // 每次创建新的作用域来解析 Scoped 服务
        using var scope = _serviceProvider.CreateScope();
        var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>();
        scopedService.Process();
    }
}

释放模式(IDisposable)

// 容器会自动释放实现了 IDisposable 的服务
// Singleton:应用关闭时释放
// Scoped:请求结束时释放
// Transient:默认不跟踪,除非从根容器解析

// 正确做法:Scoped 服务实现 IDisposable
public class AppDbContext : DbContext, IDisposable {
    // 请求结束时自动调用 Dispose
}

// 错误:从根容器解析 Transient+IDisposable 会造成内存泄漏
// var service = app.Services.GetRequiredService<ITransientDisposable>();
// 解决:从作用域解析
using var scope = app.Services.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<ITransientDisposable>();

多实现注册

// 注册多个实现
services.AddSingleton<IEventHandler, EmailHandler>();
services.AddSingleton<IEventHandler, SmsHandler>();
services.AddSingleton<IEventHandler, PushHandler>();

// 注入 IEnumerable 获取所有实现
public class EventDispatcher {
    private readonly IEnumerable<IEventHandler> _handlers;

    public EventDispatcher(IEnumerable<IEventHandler> handlers) {
        _handlers = handlers;
    }

    public async Task DispatchAsync(Event evt) {
        foreach (var handler in _handlers) {
            await handler.HandleAsync(evt);
        }
    }
}

// 使用 Keyed Services(.NET 8+)
services.AddKeyedSingleton<ICache, RedisCache>("redis");
services.AddKeyedSingleton<ICache, MemoryCache>("memory");

public class MyService {
    public MyService([FromKeyedServices("redis")] ICache cache) { }
}

常见场景

请求上下文服务

// 使用 Scoped 存储请求上下文
public class RequestContext {
    public string UserId { get; set; }
    public string TraceId { get; set; }
    public string ClientIp { get; set; }
}

// 中间件中设置上下文
app.Use(async (context, next) => {
    var requestContext = context.RequestServices.GetRequiredService<RequestContext>();
    requestContext.UserId = context.User.FindFirst("sub")?.Value;
    requestContext.TraceId = context.TraceIdentifier;
    requestContext.ClientIp = context.Connection.RemoteIpAddress?.ToString();
    await next();
});

// 在任意服务中注入使用
public class AuditService {
    private readonly RequestContext _requestContext;

    public AuditService(RequestContext requestContext) {
        _requestContext = requestContext;
    }

    public void Log(string action) {
        Console.WriteLine($"[{_requestContext.TraceId}] 用户 {_requestContext.UserId} {action}");
    }
}

工厂模式注册

// 使用工厂方法注册服务
services.AddSingleton<ISmsProvider>(sp => {
    var config = sp.GetRequiredService<IConfiguration>();
    var provider = config["Sms:Provider"];
    return provider switch {
        "aliyun" => new AliyunSmsProvider(config["Sms:AccessKey"]),
        "tencent" => new TencentSmsProvider(config["Sms:SecretId"]),
        _ => throw new InvalidOperationException($"未知的短信提供商: {provider}")
    };
});

注意事项

  • Singleton 服务必须是线程安全的,因为请求会并发访问同一实例
  • 不要从容器解析 Scoped 服务,会导致 Captive Dependency
  • Transient + IDisposable 从容器解析会造成内存泄漏
  • 构造器注入是推荐方式,避免使用 [FromServices] 属性注入
  • 避免在服务构造器中执行耗时操作,会影响启动性能
  • 使用 ValidateScopes = true 检测生命周期违规(开发环境默认开启)

进阶用法

自动注册程序集

// 扫描程序集自动注册服务
var assembly = typeof(Program).Assembly;
var serviceTypes = assembly.GetTypes()
    .Where(t => t.Name.EndsWith("Service") && !t.IsAbstract && !t.IsInterface);

foreach (var type in serviceTypes) {
    var interfaceType = type.GetInterface($"I{type.Name}");
    if (interfaceType != null) {
        services.AddScoped(interfaceType, type);
    }
}

// 使用 Scrutor 库简化
services.Scan(scan => scan
    .FromAssemblyOf<Program>()
    .AddClasses(classes => classes.Where(c => c.Name.EndsWith("Service")))
    .AsImplementedInterfaces()
    .WithScopedLifetime());

替代 DI 容器

// 使用 Autofac 替换默认容器
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(container => {
    container.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope();
    container.RegisterType<EmailService>().As<IEmailService>().SingleInstance();
});

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式