c# ASP.NET Core中的 IApplicationBuilder 和 IEndpointRouteBuilder 并发模型

来源:这里教程网 时间:2026-02-21 17:38:16 作者:

ASP.NET Core 中
IApplicationBuilder
不是线程安全的,别在中间件里并发修改它

IApplicationBuilder
是构建请求处理管道的“装配线”,只在应用启动时(
Startup.Configure
Program.cs
WebApplication
配置阶段)被顺序调用。它的
Use
UseMiddleware
Map
等方法不是为运行时并发调用设计的。

如果在某个中间件内部(比如异步任务中)偷偷调用
app.Use(...)
,会触发
InvalidOperationException
:“Builder is already in use” 或 “The builder has already been built”
IApplicationBuilder
内部持有对
RequestDelegate
链的引用,且其
Build()
方法仅执行一次;重复或并发调用会破坏管道一致性
常见误用场景:在健康检查中间件里动态注册新路由、在灰度中间件里根据 Header 注册临时终结点——这些都该移出
IApplicationBuilder
生命周期

IEndpointRouteBuilder
的线程安全性取决于具体实现,但
MapControllers()
等扩展方法不是运行时 API

IEndpointRouteBuilder
(如
WebApplication
实现的
EndpointRouteBuilder
)本身是线程不安全的,但它通常只在应用初始化阶段被使用。关键在于:所有
MapXxx()
方法(
MapControllerRoute
MapRazorPages
MapHub
)都只是往内部路由表注册终结点描述符(
EndpointDataSource
),不涉及实时请求分发。

这些注册操作必须在
app.MapXXX()
endpoints.MapXXX()
调用期间完成,不能在
HttpContext.RequestServices
解析出的任意服务中调用
若需运行时动态添加路由(例如插件系统),应通过自定义
EndpointDataSource
+
IEndpointRouteBuilder
的底层机制实现,而非直接调用
Map
方法
MapControllers()
本质是批量扫描程序集并生成
ControllerActionEndpointConventionBuilder
,它不检查并发,但也不允许在
Build()
后再调用

真正支持运行时动态路由的是
DynamicRouteValueTransformer
EndpointDataSource

如果你需要根据请求上下文(如租户 ID、Header、查询参数)改变路由行为,

IApplicationBuilder
IEndpointRouteBuilder
都不是目标接口。正确路径是:

DynamicRouteValueTransformer
在匹配后动态改写
RouteValueDictionary
,它天然支持异步和并发请求
实现自定义
EndpointDataSource
并注入
IServiceCollection
,配合后台定时刷新或事件驱动更新终结点集合(注意:更新时需保证
ChangeToken
通知消费者)
避免在
Map
链中嵌套
app.Run(async ctx => { ... })
并在里面调用
endpoints.MapGet
—— 这会导致
InvalidOperationException: Cannot create child container from a resolved service
public class TenantRouteTransformer : DynamicRouteValueTransformer
{
    public override async ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
    {
        var tenant = httpContext.Request.Headers["X-Tenant"].FirstOrDefault();
        if (!string.IsNullOrEmpty(tenant))
        {
            values["tenant"] = tenant;
        }
        return values;
    }
}

并发模型的本质:ASP.NET Core 的请求处理是 per-request 的,但配置阶段是单次、顺序、不可重入的

整个框架把“配置”和“执行”严格分离:

IApplicationBuilder
IEndpointRouteBuilder
属于配置期对象,它们的生命周期止于
app.Build()
;之后所有并发请求都走同一个已构建好的
RequestDelegate
链,由
EndpointRoutingMiddleware
EndpointMiddleware
协同完成路由匹配与执行。

不要试图在
HttpContext
中拿到
IApplicationBuilder
实例 —— 它根本不会被注入到 DI 容器
IEndpointRouteBuilder
也不是 Scoped 或 Transient 服务,它只作为
WebApplication
的内部属性存在,无法从
HttpContext.RequestServices
获取
最易忽略的一点:即使你用
WebHostBuilder
手动构建多个
IWebHost
,每个实例的
IApplicationBuilder
仍只在其自身启动阶段有效,跨实例共享毫无意义

相关推荐