长期支持版本意味着什么

.NET 采用双轨发布:偶数版本为长期支持版本,支持周期三年;奇数版本为标准支持,只有约十八个月。选择长期支持版本意味着你能在较长周期内只接收安全修复与稳定性改进,而不必频繁跟进破坏性变更,这对生产系统是实实在在的成本节约。

版本选型的第一原则是看支持周期,第二原则才看新特性——一个已经停止维护的框架,性能再好也是资产负担。
版本升级的收益
  • 运行时优化每一代都在 JIT、集合与字符串处理上做持续改进。
  • 语言配套新版本语言特性与运行时能力同步可用,无需额外配置。
  • 容器友好官方镜像持续精简,镜像体积与启动时间都在下降。
  • 安全修复支持期内持续获得漏洞修复,无需自行打补丁。
.NET 8 配图:圆角方块堆叠的抽象构建积木与几何徽标
框架能力像积木一样层层叠加,每一层都可以独立替换。

Minimal API:更少的样板代码

Minimal API 用一组链式调用替代控制器与特性路由,适合接口数量不多、职责清晰的小型服务。路由参数从方法签名自动绑定,参数校验可以直接写在处理函数里;当接口规模变大后,再按功能拆成多个扩展方法注册,依然能保持结构清晰。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IArticleStore, MemoryArticleStore>();

var app = builder.Build();

app.MapGet("/api/articles/{id:int}", async (int id, IArticleStore store) =>
{
    var article = await store.FindAsync(id);
    return article is null ? Results.NotFound() : Results.Ok(article);
});

app.Run();

这段代码体现了框架设计的取向:依赖注入直接体现在参数上,中间件按需添加,返回值用 Results 静态方法表达状态码与内容。它比控制器版本少了一层类与特性,代价是当参数与校验规则复杂时,需要自己组织代码结构。

依赖注入与配置体系

内置容器按生命周期管理服务:单例全局唯一、作用域随请求创建、瞬态每次解析都新建。绝大多数故障源于生命周期错配——把作用域服务注入单例,容器无法提供,运行时就抛异常;反过来,把本该单例的有状态服务注册成瞬态,则会出现状态丢失或资源重复创建。

生命周期选择
  • 单例无状态工具、缓存与配置读取器,注意必须线程安全。
  • 作用域数据库上下文、请求级缓存的默认选择。
  • 瞬态轻量无状态对象,创建成本低,不要滥用。

配置体系则把命令行参数、环境变量、配置文件、密钥管理等来源合并成统一的取值接口,越靠后的来源优先级越高。这个顺序让同一份代码天然支持「本地用文件、线上用环境变量」,避免把密钥提交进仓库。

原生 AOT 与启动性能

原生 AOT 在发布时把中间语言直接编译为机器码,产物不依赖运行时即时编译,启动时间可降到毫秒级,内存占用也更低,非常适合容器与命令行工具。代价是它要求代码路径在编译期可静态分析:不能依赖运行时代码生成,反射需要显式声明,部分动态特性不可用。

dotnet publish -c Release -r linux-x64 --self-contained \
  /p:PublishAot=true /p:InvariantGlobalization=true

如果项目大量依赖反射或动态代理,强行开启 AOT 会付出很高的改造代价。这时更务实的选择是「裁剪 + 就绪运行」:裁剪掉未使用的程序集减小体积,把耗时的初始化提前到发布阶段完成,从而缩短首次请求的响应时间。

容器化部署与可观测性

容器化推荐使用多阶段构建:构建阶段安装 SDK 完成编译,运行阶段只拷贝发布产物并使用精简的基础镜像。这样镜像里既没有源码也没有编译工具,体积小、攻击面小,构建缓存也更容易命中。运行时通过环境变量注入配置,进程以非 root 用户启动,健康检查指向一个轻量接口。

可观测性三件套应当在上线前接好:结构化日志记录请求标识与耗时、指标暴露请求量延迟与错误率、分布式追踪串联跨服务调用。缺少任何一项,线上排查都只能靠猜;配齐之后,定位一个慢请求通常只需要几分钟。