Как сделать универсальный контроллер для запросов апи с помощью MediatR?

У меня есть контроллер для таблицы с учителями. Если сделать запрос на api/tutors, учителя отправятся в JSON в виде ответа.

Controller

[ApiController]
public sealed class TutorsController : Controller
{
    private readonly IMediator mediator;
    private readonly IMapper mapper;

    public TutorsController(IMediator mediator, IMapper mapper)
    {
        this.mediator = mediator;
        this.mapper = mapper;
    }

    [HttpGet("api/tutors")]
    public async Task<IActionResult> Get([FromQuery] int page = 0, [FromQuery] int size = 30)
    {        
        var getTutorsQuery = new GetTutorsQuery(page, size); // строка с комментарием
        var tutors = await mediator.Send(getTutorsQuery);
        return Ok(mapper.Map<PageDto<TutorDto>>(tutors));
    }
}

GetTutorsQuery

internal sealed class GetTutorsQuery : IRequest<Page<Tutor>>
{
    public GetTutorsQuery(long pageNumber, long pageSize)
    {
        PageNumber = pageNumber;
        PageSize = pageSize;
    }
    
    public long PageSize { get; }
    public long PageNumber { get; }
}

GetStudentsQueryHandler

[UsedImplicitly]
internal sealed class GetStudentsQueryHandler : IRequestHandler<GetTutorsQuery, Page<Tutor>>
{
    private readonly ICrudRepository<Tutor> tutorsRepository;

    public GetStudentsQueryHandler(ICrudRepository<Tutor> tutorsRepository)
    {
        this.tutorsRepository = tutorsRepository;
    }

    public async Task<Page<Tutor>> Handle(GetTutorsQuery request, CancellationToken cancellationToken)
    {
        return await tutorsRepository.Get(request.PageNumber, request.PageSize);
    }
}

Сейчас мне нужно сделать так, чтобы при запросе на api/students и api/lessons также возвращались данные по такому же принципу как у учителей. Можно сделать несколько контроллеров, но тогда будет слишком много повторяющегося кода. Можно ли как-то упростить контроллеры с помощью дженериков?

Пробовал сделать вместо new GetTutorsQuery (Controller, строка с комментарием) сделать new GetQuery<Tutor>. В GetTutorsQuery и GetStudentsQueryHandler заменил все упоминания Tutor на T (ну и в названия классов тоже добавил <T>). Но в таком случае возникает ошибка

fail: 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[SPA.Application.Queri
es.GetQuery`1[SPA.Models.Tutor],SPA.Models.Page`1[SPA.Models.Tutor]]. Register your handlers with the container. See the samples in Gi
tHub for examples.
       ---> System.InvalidOperationException: No service for type 'MediatR.IRequestHandler`2[SPA.Application.Queries.GetQuery`1[SPA.Mo
dels.Tutor],SPA.Models.Page`1[SPA.Models.Tutor]]' has been registered.
         at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Ty
pe 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, CancellationT
oken cancellationToken)
         at MediatR.Pipeline.RequestExceptionProcessorBehavior`2.Handle(TRequest request, RequestHandlerDelegate`1 next, CancellationT
oken cancellationToken)
         at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, RequestHandlerDelegate`1 next, Cancell
ationToken cancellationToken)
         at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, RequestHandlerDelegate`1 next, Cancell
ationToken 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 c
ancellationToken)
         at SPA.V1.Controllers.V1TutorsController.Get(Int32 page, Int32 size) in C:\Users\aesok\Desktop\tutoraggregator\SPA\V1\Control
lers\V1TutorsController.cs:line 34
         at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper m
apper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionI
nvoker invoker, ValueTask`1 actionResultValueTask)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerAct
ionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& is
Completed)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker,
 Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isComplete
d)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, I
Disposable scope)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, I
Disposable scope)
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogg
er logger)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)


Ответы (0 шт):