柏虎资源网

专注编程学习,Python、Java、C++ 教程、案例及资源

3分钟搞懂ASP.NET Core依赖注入:Singleton/Scoped/Transient实战

引言:为什么你的代码越改越乱?

最近接手一个老项目,发现Controller里堆满了new DatabaseContext()new RedisClient(),改一个功能要动五六个地方,Bug比头发还多!后来才发现,没用好依赖注入(DI) 是原罪。

今天用大白话+代码实例,带你彻底搞懂ASP.NET Core的三种服务生命周期,从此写出清爽可维护的代码!

一、什么是依赖注入?用人话解释

依赖注入 = "外卖模式"

  • 你(类)需要吃饭(服务),不用自己买菜做饭(new对象),直接点外卖(DI容器),小哥(框架)会把热乎的饭菜(实例)送到你手上。

核心好处

  • 解耦代码:改服务实现不用动调用方
  • 自动管理:用完自动回收,不担心内存泄漏
  • 方便测试:轻松替换成模拟服务

二、三分钟分清三种生命周期(重点)

生命周期

通俗理解

举个栗子

适合场景

Singleton

全局唯一"单例"

公司前台(所有人共用一个)

配置、缓存、日志服务

Scoped

一次请求"专属"

餐厅服务员(一桌一个)

数据库上下文(DbContext)、用户会话

Transient

用完就扔"临时工"

一次性筷子(用一次换一个)

工具类(加密、邮件发送)

形象对比


Singleton像公司饮水机,Scoped像你工位的杯子,Transient像一次性纸杯

三、代码实战:注册与使用

1. 注册服务(Program.cs中)

var builder = WebApplication.CreateBuilder(args);

// 单例:全局缓存(整个应用共用一个)
builder.Services.AddSingleton<ICacheService, MemoryCacheService>();

// 作用域:数据库上下文(每个请求一个新实例)
builder.Services.AddScoped<AppDbContext>();

// 瞬态:邮件服务(每次调用新建实例)
builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();

2. 使用服务(控制器中)

public class OrderController : Controller
{
    private readonly ICacheService _cache;
    private readonly AppDbContext _dbContext;

    // 构造函数注入(推荐)
    public OrderController(ICacheService cache, AppDbContext dbContext)
    {
        _cache = cache;       // 单例服务
        _dbContext = dbContext; // 作用域服务
    }

    public IActionResult GetOrder(int id)
    {
        // 瞬态服务:需要时通过方法注入
        var emailSender = HttpContext.RequestServices.GetService<IEmailSender>();
        emailSender.Send("订单查询通知", #34;用户查询了订单{id}");

        return Ok(_dbContext.Orders.Find(id));
    }
}

四、避坑指南:90%的人会犯的3个错误

1. 生命周期不匹配(最致命)


错误代码:在单例服务中注入作用域服务

//  错误示范
services.AddSingleton<OrderService>();
services.AddScoped<AppDbContext>();

public class OrderService 
{
    // 单例依赖作用域服务,会导致上下文被释放后使用
    public OrderService(AppDbContext dbContext) { ... }
}

解决:改用IServiceScopeFactory手动创建作用域

2. 循环依赖
服务A依赖B,B又依赖A,导致DI容器无法解析。
解决

  • 拆分成更小的服务
  • Lazy<T>延迟加载依赖

3. 过度使用Transient
频繁创建实例会增加性能开销,
无状态且轻量的服务才用Transient

五、老司机的3个最佳实践

  1. 按"使用频率"选择生命周期
  2. 全局共享→Singleton
  3. 请求内共享→Scoped
  4. 单次操作→Transient
  5. 优先用构造函数注入
    避免
    [FromServices]属性注入,显式声明依赖更清晰。
  6. 用接口隔离服务
    注册时用接口而非具体类,例如
    AddScoped<IDbContext, AppDbContext>(),方便后续替换实现。

总结:一句话记住

"单例全局用,作用域随请求,瞬态单次用"

你在项目中踩过依赖注入的坑吗?评论区分享你的经历,点赞最高送《.NET性能优化手册》!


参考资料

  • Microsoft Learn官方文档
  • ASP.NET Core依赖注入最佳实践

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言