

新闻资讯
技术学院MediatR 的 IMediator 默认注册为瞬时(Transient),每次解析都创建新实例;其线程安全性取决于 handler 的实现与生命周期配置,错误地将 Scoped 服务注入 Singleton handler 会导致运行时异常。
在 ASP.NET Core 中,MediatR 的 IMediator 接口默认通过 AddMediatR() 扩展方法注册为 ServiceLifetime.Transient。这意味着每次从 DI 容器解析 IMediator 时,都会创建一个新实例。
它本身不持有跨请求的共享状态,但它的行为依赖于你注册的 handlers 和 pipeline behaviors:
INotificationHandler、IRequestHandler 等 handler 类型,默认也按 Transient 注册(除非你显式改用 Scoped 或 Singleton)Singleton,而它内部又持有非线程安全的状态(比如普通字段、静态集合),那就可能出问题MediatR 内部使用 IServiceProvider 解析 handler,所以 handler 的生命周期必须 ≥ IMediator 实例的生命周期(否则会抛 InvalidOperationException)MediatR 核心类型(如 Mediator 类)本身是无状态的,所有操作都委托给 DI 解析出的 handler,因此 IMediator 实例可被多线程并发调用 —— 这是安全的。
真正决定线程安全的是你写的 handler:
static List Cache = new() 并直接 Add,就会发生竞态DbContext(Scoped),而你把它错误注册为 Singleton,运行时可能抛 InvalidOperationException: A second operation started on this context before a previous operation completed
这是最典型的生命周期冲突,会导致运行时异常或数据错乱。例如:
public class MyHandler : IRequestHandler{ private readonly A pplicationDbContext _db; public MyHandler(ApplicationDbContext db) // ← DbContext 是 Scoped { _db = db; } public Task
Handle(MyQuery request, CancellationToken ct) { return _db.Users.CountAsync(ct); } }
如果你这样注册:
services.AddSingleton, MyHandler>(); // ❌ 错误
就会导致同一个 MyHandler 实例被多个请求复用,而它持有的 _db 是 Scoped,早已被释放或正在被其他线程使用。
正确做法是让 MediatR 自动发现并注册为 Transient:
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(MyHandler).Assembly)); // ✅ 默认就是 Transient
有时候你会想“让某个 handler 全局只初始化一次”,比如加载配置、连接第三方 SDK。这时不要把 handler 设为 Singleton,而是:
Singleton service 中(如 ISmsClientPool)这样既满足复用需求,又不破坏 DI 安全边界。MediatR 的设计哲学就是“消息即操作”,不是“消息即状态容器”。
真正容易被忽略的是:handler 的构造函数执行时机和作用域,远比 IMediator 本身的生命周期更关键。别盯着 MediatR 是单例还是瞬时,先盯紧你注册的每一个 handler 类型及其依赖树。