从 0 手写ASP.NET Core 底层组件,掌握 IOC、Middleware、EFCore,项目开发直接降 BUG
- C#
- -405分钟前
- 4热度
- 0评论
来源: 从 0 手写ASP.NET Core 底层组件,掌握 IOC、Middleware、EFCore,项目开发直接降 BUG
-
服务注册就写 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)
- 单例依赖作用域服务报错
:单例在根容器,作用域服务在子容器,单例解析时子容器还没创建 - 内存泄漏
:瞬时服务如果实现了 IDisposable,容器会追踪引用,导致无法 GC - 循环依赖
:构造函数注入时 A 依赖 B,B 依赖 A,递归解析死循环
二、手写 Middleware 管道:揭秘俄罗斯套娃模型
2.1 中间件管道本质
ASP.NET Core 的中间件不是 "列表",而是嵌套的委托链,像俄罗斯套娃一样:
- 请求阶段
:next () 之前,从上到下依次执行 - 响应阶段
:next () 之后,从下到上反向执行 - 短路机制
:不调用 next (),请求直接返回
2.4 顺序 BUG 的底层原因
为什么中间件顺序不能乱?
因为管道是严格的嵌套结构:
-
异常处理中间件必须放最前面:才能捕获后面所有中间件的异常 -
静态文件中间件要放路由前面:避免不必要的路由匹配 -
认证中间件要放授权前面:没认证谈什么授权
三、手写 EF Core 核心:ORM 不就是 SQL 生成器吗?
3.1 EF Core 三大核心机制
很多人用了多年 EF Core,不知道底层就三件事:
- LINQ 表达式树解析
:把 C# 代码翻译成 SQL - 变更追踪
:跟踪实体状态,生成对应的增删改语句 - 对象映射
:把数据行映射成实体对象
- AsNoTracking 为什么能提升性能?
跳过了快照拷贝和变更检测 - 为什么不能在循环里多次 SaveChanges?
每次都要遍历全部追踪实体做变更检测 - 为什么同一个实体不能被两个上下文追踪?
每个上下文有独立的追踪器,主键冲突
四、为什么懂底层的人,写的代码 BUG 更少?
4.1 排查问题速度差 10 倍
别人还在百度 "这个异常是什么意思",你已经知道:
-
看到 Cannot consume scoped service from singleton→ 生命周期不匹配 -
看到 ObjectDisposedException→ 作用域释放了但还在用里面的服务 -
看到 Tracking相关异常 → 同一个实体被多个实例追踪
-
不会写出 "单例服务里依赖 HttpContext" 这种代码 -
不会把授权中间件放到异常处理前面 -
不会在循环里反复查询却忘了加 AsNoTracking
4.3 性能优化有方向
懂了底层原理,优化不是靠 "猜",而是靠 "算":
-
IOC:减少构造函数参数数量,降低反射解析开销 -
中间件:精简管道,能短路就短路 -
EF Core:预估生成的 SQL 是什么样,索引有没有命中