c# MediatR 在ASP.NET Core中是单例还是瞬时 线程安全吗

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

MediatR 默认注册为瞬时(Transient)

在 ASP.NET Core 中,

MediatR
IMediator
接口默认通过
AddMediatR()
扩展方法注册为
ServiceLifetime.Transient
。这意味着每次从 DI 容器解析
IMediator
时,都会创建一个新实例。

它本身不持有跨请求的共享状态,但它的行为依赖于你注册的 handlers 和 pipeline behaviors:

INotificationHandler<t></t>
IRequestHandler<trequest tresponse></trequest>
等 handler 类型,默认也按
Transient
注册(除非你显式改用
Scoped
Singleton
如果你把某个 handler 注册为
Singleton
,而它内部又持有非线程安全的状态(比如普通字段、静态集合),那就可能出问题
MediatR
内部使用
IServiceProvider
解析 handler,所以 handler 的生命周期必须 ≥
IMediator
实例的生命周期(否则会抛
InvalidOperationException

MediatR 是线程安全的,但 handler 不一定

MediatR
核心类型(如
Mediator
类)本身是无状态的,所有操作都委托给 DI 解析出的 handler,因此
IMediator
实例可被多线程并发调用 —— 这是安全的。

真正决定线程安全的是你写的 handler:

如果 handler 是纯函数式(只读参数、不改内部字段、不操作静态变量),那它是线程安全的 如果 handler 里用了
static List<int> Cache = new()</int>
并直接 Add,就会发生竞态
如果 handler 依赖
DbContext
(Scoped),而你把它错误注册为 Singleton,运行时可能抛
InvalidOperationException: A second operation started on this context before a previous operation completed

常见错误:在 Singleton handler 中注入 Scoped 服务

这是最典型的生命周期冲突,会导致运行时异常或数据错乱。例如:

public class MyHandler : IRequestHandler<MyQuery, string>
{
    private readonly ApplicationDbContext _db;
    public MyHandler(ApplicationDbContext db) // ← DbContext 是 Scoped
    {
        _db = db;
    }
    public Task<string> Handle(MyQuery request, CancellationToken ct)
    {
        return _db.Users.CountAsync(ct);
    }
}

如果你这样注册:

services.AddSingleton<IRequestHandler<MyQuery, string>, MyHandler>(); // ❌ 错误

就会导致同一个

MyHandler
实例被多个请求复用,而它持有的
_db
是 Scoped,早已被释放或正在被其他线程使用。

正确做法是让 MediatR 自动发现并注册为 Transient:

services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(MyHandler).Assembly)); // ✅ 默认就是 Transient

需要单例行为?别改 MediatR,改你的设计

有时候你会想“让某个 handler 全局只初始化一次”,比如加载配置、连接第三方 SDK。这时不要把 handler 设为 Singleton,而是:

把需要共享的状态提取到单独的
Singleton
service 中(如
ISmsClientPool
在 handler 中注入该 Singleton 服务 handler 本身仍保持 Transient,避免生命周期污染

这样既满足复用需求,又不破坏 DI 安全边界。MediatR 的设计哲学就是“消息即操作”,不是“消息即状态容器”。

真正容易被忽略的是:handler 的构造函数执行时机和作用域,远比

IMediator
本身的生命周期更关键。别盯着 MediatR 是单例还是瞬时,先盯紧你注册的每一个 handler 类型及其依赖树。

相关推荐