Autofac在MediatR后台通知处理器执行完成前提前释放DbContext如何解决
解决MediatR后台通知DbContext已释放问题的正确方案
核心问题复盘
问题根源是:默认MediatR解析Handler使用当前Web请求的作用域,你把Handler丢到Task.Run后台执行时,请求结束后作用域被销毁,依赖的DbContext、仓储等服务也会被提前释放。你之前直接修改ServiceFactory从根作用域创建全局作用域的方案会引发资源泄漏、线程安全问题,不可长期使用。
正确实现步骤
1. 重构CustomMediator,为每个后台Handler单独创建独立作用域
不要全局修改ServiceFactory的解析逻辑,仅在发布后台通知时为每个任务单独创建作用域,执行完成后自动销毁:
public class CustomMediator : Mediator { private readonly ILifetimeScope _rootLifetimeScope; // 注入根生命周期作用域 public CustomMediator(ServiceFactory serviceFactory, ILifetimeScope lifetimeScope) : base(serviceFactory) { _rootLifetimeScope = (lifetimeScope as LifetimeScope).RootLifetimeScope; } protected override Task PublishCore(IEnumerable<Func<INotification, CancellationToken, Task>> allHandlers, INotification notification, CancellationToken cancellationToken) { foreach (var handler in allHandlers) { _ = Task.Run(async () => { // 为每个后台任务单独创建作用域,执行完自动释放 await using var handlerScope = _rootLifetimeScope.BeginLifetimeScope(); // 从新作用域解析Handler,不使用请求作用域创建的实例 var notificationType = notification.GetType(); var handlerType = typeof(INotificationHandler<>).MakeGenericType(notificationType); var handlerInstance = handlerScope.Resolve(handlerType) as INotificationHandler<INotification>; if (handlerInstance != null) { await handlerInstance.Handle(notification, cancellationToken); } }, cancellationToken); } return Task.CompletedTask; } }
如果需要区分普通同步通知和后台通知,可以新增空的标记接口IBackgroundNotification继承INotification,仅实现该接口的通知走后台作用域逻辑,不影响原有同步通知的正常逻辑。
2. 保留现有注入配置
你当前的DbContext、仓储、MediatR相关注入配置不需要修改,每个独立作用域会自动创建新的Scoped生命周期实例(包括DbContext),不会和请求作用域冲突,也没有线程安全问题。
可选优化
如果你的后台任务执行时间较长、对可靠性要求高,可以搭配ASP.NET Core自带的IHostedService实现任务队列,先把通知存入队列,由后台常驻服务消费执行,避免应用重启时丢失未完成的任务。
内容的提问来源于stack exchange,提问作者Dvornik Xilosof
相关产品推荐
相关产品推荐

