从 0 手写ASP.NET Core 底层组件,掌握 IOC、Middleware、EFCore,项目开发直接降 BUG

来源: 从 0 手写ASP.NET Core 底层组件,掌握 IOC、Middleware、EFCore,项目开发直接降 BUG

很多.NET 开发者写了三五年代码,依然停留在 "调用 API" 的层面:
  • 服务注册就写builder.Services.AddScoped,但为什么会有生命周期问题?
  • 中间件就按顺序app.UseXXX,但为什么顺序错了功能就异常?
  • EF Core 就写dbContext.SaveChanges,但为什么会出现追踪混乱、性能雪崩?

本质原因:你只知其然,不知其所以然。

当你亲手写过一遍底层核心组件,再看官方实现时,一切 BUG 根源都会变得一目了然。今天我们从 0 手写三大核心组件,彻底打通ASP.NET Core 底层任督二脉。


一、手写 IOC 容器:彻底搞懂依赖注入的本质

1.1 IOC 核心原理

IOC(控制反转)的本质就是一个 "菜谱 + 厨师" 模型:

  • IServiceCollection
     = 菜谱,记录每道菜的做法
  • ServiceDescriptor
     = 菜品描述,包含菜名、做法、上菜方式
  • IServiceProvider
     = 厨师,根据菜谱做出菜来
  • 生命周期
     = 上菜规则:做一次一直用(Singleton)、每桌一份(Scoped)、每点一份做一份(Transient)
1.2 完整手写迷你 DI 容器
图片
1.3 写完你就懂的 BUG 根源
  • 单例依赖作用域服务报错
    :单例在根容器,作用域服务在子容器,单例解析时子容器还没创建
  • 内存泄漏
    :瞬时服务如果实现了 IDisposable,容器会追踪引用,导致无法 GC
  • 循环依赖
    :构造函数注入时 A 依赖 B,B 依赖 A,递归解析死循环

二、手写 Middleware 管道:揭秘俄罗斯套娃模型

2.1 中间件管道本质

ASP.NET Core 的中间件不是 "列表",而是嵌套的委托链,像俄罗斯套娃一样:

图片
每个中间件都有两次执行机会:
  • 请求阶段
    :next () 之前,从上到下依次执行
  • 响应阶段
    :next () 之后,从下到上反向执行
  • 短路机制
    :不调用 next (),请求直接返回
2.2 手写中间件管道
图片
2.3 测试运行
图片

2.4 顺序 BUG 的底层原因

为什么中间件顺序不能乱?

因为管道是严格的嵌套结构:

  • 异常处理中间件必须放最前面:才能捕获后面所有中间件的异常
  • 静态文件中间件要放路由前面:避免不必要的路由匹配
  • 认证中间件要放授权前面:没认证谈什么授权
顺序错了不是 "不生效" 这么简单,而是整个嵌套逻辑全错。

三、手写 EF Core 核心:ORM 不就是 SQL 生成器吗?

3.1 EF Core 三大核心机制

很多人用了多年 EF Core,不知道底层就三件事:

  1. LINQ 表达式树解析
    :把 C# 代码翻译成 SQL
  2. 变更追踪
    :跟踪实体状态,生成对应的增删改语句
  3. 对象映射
    :把数据行映射成实体对象
3.2 手写简易 ORM 核心
图片
3.3 EF Core 常见 BUG 的真相
  • AsNoTracking 为什么能提升性能?
     跳过了快照拷贝和变更检测
  • 为什么不能在循环里多次 SaveChanges?
     每次都要遍历全部追踪实体做变更检测
  • 为什么同一个实体不能被两个上下文追踪?
     每个上下文有独立的追踪器,主键冲突

四、为什么懂底层的人,写的代码 BUG 更少?

4.1 排查问题速度差 10 倍

别人还在百度 "这个异常是什么意思",你已经知道:

  • 看到Cannot consume scoped service from singleton → 生命周期不匹配
  • 看到ObjectDisposedException → 作用域释放了但还在用里面的服务
  • 看到Tracking相关异常 → 同一个实体被多个实例追踪
4.2 从根源上避免设计错误
  • 不会写出 "单例服务里依赖 HttpContext" 这种代码
  • 不会把授权中间件放到异常处理前面
  • 不会在循环里反复查询却忘了加AsNoTracking
     

4.3 性能优化有方向

懂了底层原理,优化不是靠 "猜",而是靠 "算":

  • IOC:减少构造函数参数数量,降低反射解析开销
  • 中间件:精简管道,能短路就短路
  • EF Core:预估生成的 SQL 是什么样,索引有没有命中