.NET Core 7 最小 API MediatR IRequest 处理程序映射错误

3

当我通过 .NET Core 7 Minimal API 执行命令时,目前出现以下错误:

 Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
      An unhandled exception has occurred while executing the request.
      System.InvalidOperationException: Error constructing handler for request of type MediatR.IRequestHandler`2[mNet.FileServer.Commands.Core.Application.Features.
StoredFileTypes.NewStoredFileType.NewStoredFileTypeCommand,mNet.Common.Core.Application.Commands.BaseCommandResponse`1[mNet.FileServer.Shared.Core.Domain.StoredFile
Types.ValueObjects.StoredFileTypeId]]. Register your handlers with the container. See the samples in GitHub for examples.
       ---> System.InvalidOperationException: No service for type 'MediatR.IRequestHandler`2[mNet.FileServer.Commands.Core.Application.Features.StoredFileTypes.NewS
toredFileType.NewStoredFileTypeCommand,mNet.Common.Core.Application.Commands.BaseCommandResponse`1[mNet.FileServer.Shared.Core.Domain.StoredFileTypes.ValueObjects.S
toredFileTypeId]]' has been registered.
         at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
         at MediatR.ServiceFactoryExtensions.GetInstance[T](ServiceFactory factory)
         at MediatR.Wrappers.HandlerBase.GetHandler[THandler](ServiceFactory factory)
         --- End of inner exception stack trace ---
         at MediatR.Wrappers.HandlerBase.GetHandler[THandler](ServiceFactory factory)
         at MediatR.Wrappers.RequestHandlerWrapperImpl`2.<>c__DisplayClass1_0.<Handle>g__Handler|0()
         at MediatR.Pipeline.RequestExceptionProcessorBehavior`2.Handle(TRequest request, RequestHandlerDelegate`1 next, CancellationToken cancellationToken)
         at MediatR.Pipeline.RequestExceptionProcessorBehavior`2.Handle(TRequest request, RequestHandlerDelegate`1 next, CancellationToken cancellationToken)
         at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, RequestHandlerDelegate`1 next, CancellationToken cancellationToken)
         at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, RequestHandlerDelegate`1 next, CancellationToken cancellationToken)
         at MediatR.Pipeline.RequestPostProcessorBehavior`2.Handle(TRequest request, RequestHandlerDelegate`1 next, CancellationToken cancellationToken)
         at MediatR.Pipeline.RequestPreProcessorBehavior`2.Handle(TRequest request, RequestHandlerDelegate`1 next, CancellationToken cancellationToken)
         at mNet.FileServer.Commands.Ui.MinimalApi.Endpoints.StoredFileTypeEndpoint.NewStoredFileTypeAsync(NewStoredFileTypeDto request, IMediator mediator) in C:\U
sers\dcmea\OneDrive\mNet Microservices\mNet.FileServer\mNet.FileServer.Commands.Ui.MinimalApi\Endpoints\StoredFileTypeEndpoint.cs:line 47
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.<ExecuteTaskOfT>g__ExecuteAwaited|111_0[T](Task`1 task, HttpContext httpContext)
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass89_2.<<HandleRequestBodyAndCompileRequestDelegateForJson>b__2>d.MoveNext()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
         at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
         at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

代码的关键部分在以下项目中,具体如下:

  • mNet.Common.Core.Application:包含BaseCommandBaseCommandResponse
  • mNet.FileServer.Shared.Core.Domain:包含StoredFileTypeId值对象
  • mNet.FileServer.Commands.Core.Domain:包含StoredFileType聚合
  • mNet.FileServer.Commands.Core.Application:包含NewStoredFileTypeCommandNewStoredFileTypeCommandHandler
  • mNet.FileServer.Commands.Ui.MinimalApi:包含主要的Program.cs和最小API端点

关键类包括:

Program.cs(摘录)

builder.Services.AddMediatR(AppDomain.CurrentDomain.GetAssemblies());

极简 API 端点

private static async Task<BaseCommandResponse<StoredFileTypeId>> NewStoredFileTypeAsync(NewStoredFileTypeDto request, IMediator mediator)
    {
        var messageId = Guid.NewGuid();
        var command = new NewStoredFileTypeCommand
        {
            Id = new MessageId(messageId),
            CorrelationId = new CorrelationId(messageId),
            CausationId = new CausationId(messageId),
            CommandDto = request
        };

        var response = await mediator.Send(command); //Error happens after this MediatR call and is line 47 as referenced in the error
        return response;

    }

新存储文件类型命令

public class NewStoredFileTypeCommand : BaseCommand, IRequest<BaseCommandResponse<StoredFileTypeId>>
{
    public NewStoredFileTypeDto CommandDto { get; init; } = default!;
}

新存储文件类型命令处理程序

public class NewStoredFileTypeCommandHandler : IRequestHandler<NewStoredFileTypeCommand, BaseCommandResponse<StoredFileTypeId>>
{
    private readonly IMapper _mapper;
    private readonly IEventSourcingHandler<StoredFileType, StoredFileTypeId> _eventSourcingHandler;

    public NewStoredFileTypeCommandHandler(IMapper mapper,
        IEventSourcingHandler<StoredFileType, StoredFileTypeId> eventSourcingHandler)
    {
        _mapper = mapper;
        _eventSourcingHandler = eventSourcingHandler;
    }

    public async Task<BaseCommandResponse<StoredFileTypeId>> Handle(NewStoredFileTypeCommand request, CancellationToken cancellationToken)
    {
       var response = new BaseCommandResponse<StoredFileTypeId>();

        var aggregate = new StoredFileType(
            new StoredFileTypeId(Guid.NewGuid()), 
            request.CorrelationId, 
            new CausationId(request.Id.Id),
            request.CommandDto.Name, 
            request.CommandDto.IsImageFileType,
            _mapper.Map<BootstrapIconCode>(request.CommandDto.BootstrapIconCode),
            _mapper.Map<MimeType>(request.CommandDto.MimeType)
            );
        
        await _eventSourcingHandler.SaveAsync(aggregate);

        response.Id = aggregate.Id;
        response.Message = $"Created new {nameof(StoredFileType)} aggregate!";
        response.IsSuccessful = true;

        return response;


    }

从进行一些调试跟踪来看,似乎 MediatR 没有将 NewStoredFileTypeCommandHandler 映射到 NewStoredFileTypeCommand,而我无法弄清楚原因。
另外,由于在 Program.cs 中声明了 AppDomain.CurrentDomain.GetAssemblies(),所以它不应该是范围的问题。
如果有任何想法,将不胜感激,因为这让我疯了!
1个回答

3

我的猜测基于AppDomain.GetAssemblies文档中的以下引用:

获取已加载到此应用程序域的执行上下文中的程序集。

有可能对应的程序集尚未加载到应用程序域中。尝试使用AddMediatr并提供存储mediatr部分的程序集/类型:

// use a type per assembly containing the MediatR components
builder.Services.AddMediatR(typeof(NewStoredFileTypeCommandHandler), typeof(BaseCommand), ...);

谢谢 - 那完美地解决了问题!有没有办法强制加载程序集?这样每个命令处理程序都必须添加那一行就会很麻烦。 - dmeadley
@dmeadley - 很高兴能帮忙!如果答案对您有用,请随意将其标记为已接受的答案)_"有没有一种方法可以强制加载程序集"_ - 是的,您需要使用其中的类型。_"每个命令处理程序都必须添加那行代码会很麻烦"_ - 据我所知,您不需要这样做,每个程序集只需要一个(据我所记,Mediatr使用类型来获取其程序集并加载所有所需的Mediatr组件)。 - Guru Stron
谢谢@Guru Stron。我会看看如何在加载时使用它的类型,以避免需要记住每个程序集添加一个。 - dmeadley
1
@dmeadley typeof(SomeType) - 实际上是一种用法。我建议坚持答案中的代码,因为在“需要记住”的方面没有区别。而仅仅为了在AddMediatR之前加载程序集而使用类型用法是相当脆弱的方法(依赖于顺序、运行时行为等)。我认为每个程序集一个类型的AddMediatR方法更加清晰和可维护。 - Guru Stron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接