.NET 6 WebApi如何缓存程序集版本避免中间件重复反射调用
实现方案
核心逻辑:程序集版本在应用启动后不会发生变化,只需在启动阶段执行1次反射获取版本值,后续所有请求直接复用内存中缓存的字符串即可,完全避免重复反射开销。
方案1:改造现有中间件(改动最小,零额外依赖)
ASP.NET Core 自定义中间件默认是单例生命周期,构造函数仅在应用启动时执行1次,你只需要把反射获取版本的逻辑移到构造函数中存为私有只读字段,不需要额外写静态类就能实现一次获取全局复用。
改造后的中间件代码:
public class CustomHeaderMiddleware { private readonly RequestDelegate _next; // 版本字符串仅初始化一次,所有请求复用 private readonly string _appVersion; public CustomHeaderMiddleware(RequestDelegate next) { _next = next; // 仅在中间件实例化(应用启动)时执行1次反射 _appVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown"; } public async Task Invoke(HttpContext context) { // 直接读取预存的字符串,无反射开销 context.Response.Headers.TryAdd("Version", _appVersion); await _next(context); } }
小提示:把原来的
Headers.Add换成Headers.TryAdd,避免后续其他逻辑重复添加同名响应头时抛出异常,容错性更好。
方案2:静态类存储(适合多场景复用版本号)
如果你除了给响应头加版本,还需要在接口返回、日志打印、异常提示等位置用到应用版本,可以定义静态类在启动时初始化一次版本信息,全局任意位置直接访问。
首先定义静态存储类:
public static class AppRuntimeInfo { public static string Version { get; private set; } = "unknown"; // 启动阶段调用一次即可完成初始化 public static void Init() { Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown"; } }
然后在Program.cs中启动时初始化,中间件直接读取静态值即可:
var builder = WebApplication.CreateBuilder(args); // 注册控制器、其他服务... builder.Services.AddControllers(); var app = builder.Build(); // 应用启动时执行一次版本初始化 AppRuntimeInfo.Init(); // 中间件直接复用静态值,无重复反射 app.Use(async (context, next) => { context.Response.Headers.TryAdd("Version", AppRuntimeInfo.Version); await next(); }); // 其他中间件配置:授权、路由映射等 app.UseAuthorization(); app.MapControllers(); app.Run();
方案3:Program.cs 内联实现(最轻量,无需额外类文件)
如果项目比较简单,不想额外新增中间件类或者静态类,可以直接在Program.cs启动阶段获取一次版本号,用内联中间件实现响应头注入,所有逻辑集中在启动文件中。
核心代码如下:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); var app = builder.Build(); // 仅在这里执行1次反射获取版本 var apiVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown"; // 内联中间件直接复用局部变量,无额外开销 app.Use(async (context, next) => { context.Response.Headers.TryAdd("Version", apiVersion); await next(); }); app.UseAuthorization(); app.MapControllers(); app.Run();
方案选择参考
- 方案1改动成本最低,只需要调整现有中间件的几行代码,不需要修改启动注册逻辑
- 方案2灵活性最高,版本信息可以在整个应用的任意位置复用,适合中大型项目
- 方案3代码最精简,不需要新增任何额外类文件,适合小型服务或快速验证场景
以上三个方案的反射操作都仅在应用启动阶段执行1次,后续请求处理只是读取内存中的字符串,完全消除了原实现每次请求反射的性能损耗。
内容的提问来源于stack exchange,提问作者Anshul
相关产品推荐
相关产品推荐

