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

通过ICarterModule构造注入ISender时,MediatR处理器无法解析作用域服务

MediatR处理器无法解析仓储:Carter模块构造注入IMediator vs 方法参数注入ISender的差异

问题原因

Carter的ICarterModule默认以单例生命周期注册,当你在模块构造函数中注入IMediator时,IMediator也会以单例实例存在。单例服务依赖根服务提供者解析依赖,若你的ISubscriptionRepository是注册为Scoped(仓储、DbContext类的常见生命周期),根容器无法正确解析绑定到请求上下文的Scoped服务,就会出现依赖解析失败的错误。

而在端点方法中注入ISender时,ISender是从当前请求的Scoped服务提供者中获取的,能匹配Scoped依赖的生命周期,因此可以正常解析ISubscriptionRepository。

解决方案

方案1:推荐——在端点方法中直接注入ISender

这是最符合Minimal API和Carter设计风格的方式,完全规避生命周期不匹配问题:

public class SubscriptionModule : ICarterModule
{
    public void AddRoutes(IEndpointRouteBuilder app)
    {
        app.MapPost("/subscriptions", async (CreateSubscriptionCommand command, ISender sender) =>
        {
            var result = await sender.Send(command);
            return Results.Ok(result);
        })
        .WithName("CreateSubscription")
        .Produces<SubscriptionDto>(StatusCodes.Status200OK);
    }
}

方案2:通过IServiceProvider创建Scoped上下文(特殊场景用)

如果必须在模块构造函数中持有服务引用,可注入IServiceProvider,在请求时创建Scoped范围来获取ISender:

public class SubscriptionModule : ICarterModule
{
    private readonly IServiceProvider _serviceProvider;

    public SubscriptionModule(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void AddRoutes(IEndpointRouteBuilder app)
    {
        app.MapPost("/subscriptions", async (CreateSubscriptionCommand command) =>
        {
            // 创建Scoped范围,确保依赖解析在请求上下文内
            using var scope = _serviceProvider.CreateScope();
            var sender = scope.ServiceProvider.GetRequiredService<ISender>();
            
            var result = await sender.Send(command);
            return Results.Ok(result);
        });
    }
}

方案3:调整仓储生命周期为Singleton(不推荐)

如果你的仓储不依赖Scoped服务(如无DbContext依赖),可以将ISubscriptionRepository注册为Singleton,但这不符合大多数整洁架构中仓储的设计原则,可能引发并发问题,仅作为临时 workaround:

// Infrastructure层注册代码
services.AddSingleton<ISubscriptionRepository, SubscriptionRepository>();

验证DI注册正确性

确保你的MediatR和仓储注册逻辑无误:

  1. Application层注册MediatR:
public static IServiceCollection AddApplication(this IServiceCollection services)
{
    // 注册当前程序集中的所有MediatR处理器
    services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(CreateSubscriptionCommand).Assembly));
    return services;
}
  1. Infrastructure层注册仓储:
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
{
    // 通常注册为Scoped
    services.AddScoped<ISubscriptionRepository, SubscriptionRepository>();
    // 其他基础设施服务注册(如DbContext)
    return services;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 10:20:26