Skip to content

常见问题与解决方案 ​

🐛 MediatR 开发中的典型问题和解决方案


📖 概述 ​

在使用 MediatR 的过程中,开发者常会遇到一些典型问题。本章汇总了最常见的问题及其解决方案,帮助你快速排查和解决困难。


❌ 问题 1:无法解析 IMediator ​

症状 ​

InvalidOperationException: Unable to resolve service for type 'MediatR.IMediator' 
while attempting to activate 'YourController'.

原因 ​

  1. 未注册 MediatR 服务
  2. NuGet 包版本不匹配
  3. 程序集扫描失败

解决方案 ​

csharp
// ✅ 正确:确保在 Program.cs 或 Startup.cs 中注册
builder.Services.AddMediatR(cfg => 
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));

// ❌ 错误:忘记注册
// builder.Services.AddControllers();
// 缺少 AddMediatR

检查清单:

  • [ ] 已安装 MediatR NuGet 包
  • [ ] 已安装 MediatR.Extensions.Microsoft.DependencyInjection(如需)
  • [ ] 已调用 AddMediatR()
  • [ ] 程序集路径正确

🔍 问题 2:Handler 未被发现 ​

症状 ​

InvalidOperationException: No service for type 
'MediatR.IRequestHandler`2[CreateOrderCommand,OrderResult]' has been registered.

原因 ​

  1. Handler 不在扫描的程序集中
  2. Handler 不是 public
  3. Handler 是抽象类或接口
  4. 泛型约束不匹配

解决方案 ​

方案 1:检查程序集扫描 ​

csharp
// 调试:列出扫描到的 Handler
var assembly = typeof(Program).Assembly;
var handlers = assembly.GetTypes()
    .Where(t => t.GetInterfaces().Any(i => 
        i.IsGenericType && 
        i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>)))
    .ToList();

Console.WriteLine($"找到 {handlers.Count} 个 Handler:");
foreach (var h in handlers)
{
    Console.WriteLine($"  - {h.FullName}");
}

// 如果列表为空,说明程序集扫描有问题

方案 2:确保 Handler 是 public ​

csharp
// ✅ 正确
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
}

// ❌ 错误:internal 不会被扫描
internal class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
}

// ❌ 错误:abstract 不会实例化
public abstract class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
}

方案 3:多程序集扫描 ​

csharp
// 如果 Handler 在其他程序集
builder.Services.AddMediatR(cfg => {
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
    cfg.RegisterServicesFromAssembly(typeof(OrderHandlersMarker).Assembly);
    cfg.RegisterServicesFromAssembly(typeof(UserHandlersMarker).Assembly);
});

// 标记类(用于引用程序集)
public class OrderHandlersMarker { }

方案 4:检查泛型约束 ​

csharp
// ✅ 正确
public class MyHandler : IRequestHandler<MyCommand, MyResult>
{
}

// ❌ 错误:返回值类型不匹配
public class MyHandler : IRequestHandler<MyCommand, WrongResult>
{
}

🔄 问题 3:循环依赖 ​

症状 ​

InvalidOperationException: A circular dependency was detected for the service of type 
'IRequestHandler`2[OrderCommand,OrderResult]'.

原因 ​

Handler 之间相互依赖,形成循环:

csharp
// ❌ 错误:循环依赖
public class OrderHandler : IRequestHandler<OrderCommand>
{
    private readonly PaymentHandler _paymentHandler; // 依赖 PaymentHandler
    
    public OrderHandler(PaymentHandler paymentHandler)
    {
        _paymentHandler = paymentHandler;
    }
}

public class PaymentHandler : IRequestHandler<PaymentCommand>
{
    private readonly OrderHandler _orderHandler; // 依赖 OrderHandler
    
    public PaymentHandler(OrderHandler orderHandler)
    {
        _orderHandler = orderHandler;
    }
}

解决方案 ​

方案 1:提取共享服务(推荐) ​

csharp
// ✅ 正确:提取共享服务
public class OrderProcessingService
{
    public async Task ProcessOrder(Guid orderId) { /* ... */ }
    public async Task ProcessPayment(Guid orderId) { /* ... */ }
}

public class OrderHandler : IRequestHandler<OrderCommand>
{
    private readonly OrderProcessingService _service;
    
    public OrderHandler(OrderProcessingService service)
    {
        _service = service;
    }
}

public class PaymentHandler : IRequestHandler<PaymentCommand>
{
    private readonly OrderProcessingService _service;
    
    public PaymentHandler(OrderProcessingService service)
    {
        _service = service;
    }
}

方案 2:使用事件解耦 ​

csharp
// ✅ 正确:通过事件解耦
public class OrderHandler : IRequestHandler<OrderCommand>
{
    private readonly IMediator _mediator;
    
    public async Task Handle(OrderCommand request, CancellationToken ct)
    {
        // 处理订单
        await _orderRepo.CreateAsync(request);
        
        // 发布事件,而不是直接调用 PaymentHandler
        await _mediator.Publish(new OrderCreatedEvent { OrderId = request.OrderId }, ct);
    }
}

public class PaymentEventHandler : INotificationHandler<OrderCreatedEvent>
{
    public async Task Handle(OrderCreatedEvent notification, CancellationToken ct)
    {
        // 处理支付
        await _paymentService.ProcessAsync(notification.OrderId);
    }
}

方案 3:使用 Lazy<T> 延迟加载 ​

csharp
// ⚠️ 备选方案:仅在无法重构时使用
public class OrderHandler : IRequestHandler<OrderCommand>
{
    private readonly Lazy<PaymentHandler> _paymentHandler;
    
    public OrderHandler(Lazy<PaymentHandler> paymentHandler)
    {
        _paymentHandler = paymentHandler;
    }
    
    public async Task Handle(OrderCommand request, CancellationToken ct)
    {
        // 仅在需要时才解析
        var paymentHandler = _paymentHandler.Value;
        // ...
    }
}

⚠️ 问题 4:通知处理器异常导致中断 ​

症状 ​

一个通知处理器抛出异常,导致其他处理器不执行。

原因 ​

默认的 PublishCore 实现会在第一个异常时停止。

解决方案 ​

方案 1:自定义容错 Mediator(推荐) ​

csharp
public class ResilientMediator : Mediator
{
    private readonly ILogger<ResilientMediator> _logger;

    public ResilientMediator(IServiceProvider serviceFactory, ILogger<ResilientMediator> logger) 
        : base(serviceFactory)
    {
        _logger = logger;
    }

    protected override async Task PublishCore(
        IEnumerable<NotificationHandlerExecutor> handlerExecutors, 
        INotification notification, 
        CancellationToken cancellationToken)
    {
        foreach (var handlerExecutor in handlerExecutors)
        {
            try
            {
                await handlerExecutor.HandlerCallback(notification, cancellationToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, 
                    "通知处理器失败: {HandlerType}. 将继续执行其他处理器.",
                    handlerExecutor.HandlerInstance.GetType().Name);
                // 不抛出异常,继续执行下一个
            }
        }
    }
}

// 注册
builder.Services.AddScoped<IMediator, ResilientMediator>();

方案 2:在处理器内部捕获异常 ​

csharp
public class SendEmailHandler : INotificationHandler<OrderCreatedNotification>
{
    private readonly ILogger<SendEmailHandler> _logger;

    public async Task Handle(OrderCreatedNotification notification, CancellationToken ct)
    {
        try
        {
            await _emailService.SendAsync(notification.CustomerEmail);
        }
        catch (Exception ex)
        {
            // 记录错误但不抛出,避免影响其他处理器
            _logger.LogError(ex, "发送邮件失败,但不影响其他处理器");
        }
    }
}

🎯 问题 5:在单例中注入 Scoped 服务 ​

症状 ​

InvalidOperationException: Cannot consume scoped service 'DbContext' 
from singleton 'IMediator'.

原因 ​

MediatR 默认注册为 Transient,但如果手动改为 Singleton,会无法注入 Scoped 服务(如 DbContext)。

解决方案 ​

方案 1:保持 Transient(推荐) ​

csharp
// ✅ 正确:使用默认的 Transient
builder.Services.AddMediatR(cfg => 
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));

方案 2:改为 Scoped ​

csharp
// ✅ 可接受:Web 应用中每个请求一个实例
builder.Services.AddMediatR(cfg => {
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
    cfg.Lifetime = ServiceLifetime.Scoped;
});

方案 3:在单例中使用 IServiceScopeFactory ​

csharp
// ⚠️ 复杂方案:仅在必要时使用
public class SingletonService
{
    private readonly IServiceScopeFactory _scopeFactory;

    public SingletonService(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public async Task DoWork()
    {
        // 创建临时作用域
        using var scope = _scopeFactory.CreateScope();
        var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
        
        await mediator.Send(new MyCommand());
    }
}

🐌 问题 6:性能问题 ​

症状 ​

  • 应用启动缓慢
  • 请求处理延迟高
  • 内存占用大

原因 ​

  1. 大量 Handler 注册
  2. 管道行为过多
  3. 同步阻塞操作

解决方案 ​

方案 1:优化程序集扫描 ​

csharp
// ❌ 错误:扫描所有程序集
builder.Services.AddMediatR(cfg => 
    cfg.RegisterServicesFromAssemblies(AppDomain.CurrentDomain.GetAssemblies()));

// ✅ 正确:只扫描必要的程序集
builder.Services.AddMediatR(cfg => {
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
    cfg.RegisterServicesFromAssembly(typeof(SharedLibraryMarker).Assembly);
});

方案 2:减少管道行为数量 ​

csharp
// ❌ 错误:过多的行为
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(CachingBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(TransactionBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(AuditBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(MetricsBehavior<,>));
// ... 太多行为会影响性能

// ✅ 正确:只注册必要的行为
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

方案 3:避免同步阻塞 ​

csharp
// ❌ 错误:同步阻塞
public class SlowBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
    {
        Thread.Sleep(1000); // ❌ 阻塞线程
        return await next();
    }
}

// ✅ 正确:异步等待
public class FastBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
    {
        await Task.Delay(1000, ct); // ✅ 异步等待
        return await next();
    }
}

🧪 问题 7:单元测试困难 ​

症状 ​

  • Mock 配置复杂
  • 测试运行缓慢
  • 难以隔离被测代码

解决方案 ​

方案 1:直接测试 Handler(推荐) ​

csharp
// ✅ 正确:直接实例化 Handler,Mock 依赖
[Fact]
public async Task Test()
{
    var mockRepo = new Mock<IOrderRepository>();
    var handler = new CreateOrderHandler(mockRepo.Object);
    
    var result = await handler.Handle(command, CancellationToken.None);
    
    Assert.NotNull(result);
}

方案 2:Mock IMediator ​

csharp
// ✅ 正确:在控制器测试中 Mock Mediator
var mockMediator = new Mock<IMediator>();
mockMediator.Setup(m => m.Send(It.IsAny<CreateOrderCommand>(), It.IsAny<CancellationToken>()))
    .ReturnsAsync(new OrderResult { OrderId = Guid.NewGuid() });

var controller = new OrdersController(mockMediator.Object);

📝 问题 8:验证失败但无错误信息 ​

症状 ​

请求验证失败,但返回的错误信息不明确。

解决方案 ​

方案 1:增强验证行为 ​

csharp
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
    {
        if (_validators.Any())
        {
            var context = new ValidationContext<TRequest>(request);
            var results = await Task.WhenAll(
                _validators.Select(v => v.ValidateAsync(context, ct)));

            var failures = results.SelectMany(r => r.Errors).Where(f => f != null).ToList();

            if (failures.Count != 0)
            {
                // ✅ 提供详细的错误信息
                var errorMessages = failures.Select(f => 
                    $"{f.PropertyName}: {f.ErrorMessage}");
                
                throw new ValidationException(
                    $"验证失败:\n{string.Join("\n", errorMessages)}");
            }
        }

        return await next();
    }
}

方案 2:全局异常处理中间件 ​

csharp
app.UseExceptionHandler(appError =>
{
    appError.Run(async context =>
    {
        var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
        if (contextFeature?.Error is ValidationException vex)
        {
            context.Response.StatusCode = 400;
            await context.Response.WriteAsJsonAsync(new
            {
                error = "Validation Failed",
                details = vex.Errors.Select(e => new
                {
                    field = e.PropertyName,
                    message = e.ErrorMessage
                })
            });
        }
    });
});

🎯 快速排查清单 ​

遇到问题时,按以下步骤排查:

1. 检查服务注册 ​

csharp
// 在 Program.cs 中添加调试代码
var services = builder.Services.BuildServiceProvider();
try
{
    var mediator = services.GetRequiredService<IMediator>();
    Console.WriteLine("✅ MediatR 注册成功");
}
catch (Exception ex)
{
    Console.WriteLine($"❌ MediatR 注册失败: {ex.Message}");
}

2. 检查 Handler 发现 ​

csharp
var assembly = typeof(Program).Assembly;
var handlers = assembly.GetTypes()
    .Where(t => t.GetInterfaces().Any(i => 
        i.IsGenericType && 
        i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>)))
    .ToList();

Console.WriteLine($"找到 {handlers.Count} 个 Handler");

3. 检查管道行为 ​

csharp
var behaviors = builder.Services
    .Where(s => s.ServiceType == typeof(IPipelineBehavior<,>))
    .Select(s => s.ImplementationType?.Name)
    .ToList();

Console.WriteLine($"注册的行为: {string.Join(", ", behaviors)}");

4. 启用详细日志 ​

json
// appsettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "MediatR": "Debug",
      "YourApp": "Debug"
    }
  }
}

🎓 总结 ​

常见问题速查表 ​

问题主要原因解决方案
无法解析 IMediator未注册服务调用 AddMediatR()
Handler 未被发现程序集扫描问题检查程序集、可见性
循环依赖Handler 相互依赖提取共享服务或使用事件
通知中断异常未处理使用容错 Mediator
单例注入 Scoped生命周期冲突保持 Transient 或 Scoped
性能问题扫描过多、行为过多优化扫描、减少行为
测试困难依赖复杂直接测试 Handler
验证无提示错误信息不明确增强验证行为

最佳实践 ​

✅ 预防胜于治疗:

  • 遵循命名约定
  • 保持 Handler 单一职责
  • 使用管道行为处理横切关注点
  • 编写单元测试

✅ 快速排查:

  • 启用详细日志
  • 检查服务注册
  • 验证程序集扫描
  • 使用调试工具

💡 提示: 大部分问题都源于配置错误或设计不当,遵循最佳实践可以避免大多数问题!

Released under the CC BY-SA 4.0 License.