You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core与MediatR:能否从处理器发送请求?遇对象释放异常

在MediatR处理器中发送请求时遇到“Cannot access a disposed object”异常的解决方案

看起来你在处理MediatR嵌套请求时踩了个常见的坑——当你在AddUserHandler里调用_mediator.Send触发AddUserRepository请求时,出现了服务提供者已被释放的异常。我来帮你拆解问题并给出可行的解决办法。

问题原因

这个异常的核心是当前请求的服务范围(Service Scope)已经被释放,但你还在尝试用它来解析第二个请求的处理器。MediatR默认会在请求处理流程结束后自动释放关联的服务范围,如果你的第一个处理器的生命周期和这个scope绑定,当异步操作延迟执行时,很可能scope已经被提前回收了,导致后续调用无法获取服务。

解决方案

方案1:使用嵌套服务范围(推荐)

通过注入IServiceScopeFactory创建一个独立的嵌套scope,在这个新scope内获取MediatR实例来发送第二个请求。这样第二个请求的服务生命周期由新scope管理,不会受第一个请求的scope释放影响。

修改后的处理器代码如下:

public class AddUserHandler : IRequestHandler<AddUser, User>
{
    private readonly IUserRepository _userRepository;
    private readonly IServiceScopeFactory _serviceScopeFactory;

    public AddUserHandler(IUserRepository userRepository, IServiceScopeFactory serviceScopeFactory)
    {
        _userRepository = userRepository;
        _serviceScopeFactory = serviceScopeFactory;
    }

    public async Task<User> Handle(AddUser request, CancellationToken token)
    {
        var user = _userRepository.Add(request.User);
        
        // 创建独立的服务范围,确保第二个请求的服务能正常解析
        using var scope = _serviceScopeFactory.CreateScope();
        var scopedMediator = scope.ServiceProvider.GetRequiredService<IMediator>();
        await scopedMediator.Send(new AddUserRepository(user.Id), token);
        
        return user;
    }
}

方案2:检查DI注册的生命周期

确保你的MediatR处理器和相关服务注册为Scoped或Transient,而不是Singleton。如果处理器被注册为Singleton,它会持有一个已被释放的scope引用,后续调用必然报错。

在ASP.NET Core中的正确注册示例:

// 自动注册程序集中的所有MediatR服务(默认是Scoped)
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(AddUser).Assembly));

// 或者手动指定处理器的生命周期
services.AddScoped<IRequestHandler<AddUser, User>, AddUserHandler>();
services.AddScoped<IRequestHandler<AddUserRepository, Unit>, AddUserRepositoryHandler>();

方案3:直接集成业务逻辑(适合简单场景)

如果创建仓库的逻辑比较简单,你可以直接在AddUserHandler中注入仓库的仓储服务,跳过MediatR请求,这样能彻底避免scope问题,但会牺牲一部分代码解耦性。

示例代码:

public class AddUserHandler : IRequestHandler<AddUser, User>
{
    private readonly IUserRepository _userRepository;
    private readonly IRepositoryRepository _repositoryRepository;

    public AddUserHandler(IUserRepository userRepository, IRepositoryRepository repositoryRepository)
    {
        _userRepository = userRepository;
        _repositoryRepository = repositoryRepository;
    }

    public async Task<User> Handle(AddUser request, CancellationToken token)
    {
        var user = _userRepository.Add(request.User);
        // 直接调用创建空仓库的逻辑
        await _repositoryRepository.CreateEmptyRepository(user.Id, token);
        return user;
    }
}

总结

最推荐的是方案1,它既保留了MediatR带来的代码解耦优势,又完美解决了服务范围释放的问题。如果你的业务场景允许,方案3也是一种简洁的选择。

内容的提问来源于stack exchange,提问作者Petr Bogoyavlenskiy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:03:31