question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

QUESTION: Multiple handlers with different response types

See original GitHub issue

I’ve seen several other questions about whether or not it is good to have multiple handlers for a single request, but none of them seemed to quite address the use case I’m trying to solve. I have an API that allows for searching a database of geo-located photos. So I have my request class which contains all the available search parameters.

I want to be able to pass the same request to MediatR and get a different type of view model back depending on how the request is sent. For example:

// Request class
public class MyRequest : IRequest<ViewModel1>, IRequest<ViewModel2> {
    // search parameters
}

// Handlers
public class Handler1 : IRequestHandler<MyRequest, ViewModel1> {
    // handle the request and return ViewModel1
}
public class Handler2 : IRequestHandler<MyRequest, ViewModel2> {
    // handle the request and return ViewModel2
}

I could then send the request to the mediator and get the desired response back by using either:

_mediator.Send<ViewModel1>(new MyRequest());
/*--  or  --*/
_mediator.Send<ViewModel2>(new MyRequest());

The reason for all this is so I can have 2 endpoints in my API that both accept the same set of search parameters, have the same validation (which I’m handling with a pipeline behavior), but return results positioned slightly differently (mainly to format the location data in different ways).

I tried implementing this, but it doesn’t work. Whichever request is executed first runs fine, but the 2nd one fails because of an invalid cast exception. After some digging, I think I tracked down the issue to the following section of Mediator.cs:

public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default)
{
    if (request == null)
    {
        throw new ArgumentNullException(nameof(request));
    }

    var requestType = request.GetType();

    // I think the error is coming from here. _requestHandlers is static, so the first request is being
    // cached as having a response of ViewModel1. Then the 2nd request (with a response of
    // ViewModel2) throws an exception because of the different TResponse.
    var handler = (RequestHandlerWrapper<TResponse>)_requestHandlers.GetOrAdd(requestType,
        t => Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(requestType, typeof(TResponse))));

    return handler.Handle(request, cancellationToken, _serviceFactory);
}

So to my question. Is this something that could be supported by MediatR? Or is it a bad idea to structure my code this way?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
starx207commented, Apr 22, 2020

@lilasquared I’m using FluentValidation, so my validation pipeline behavior is setup to be injected with all IValidator<T>s for the current request. So because of that, and ASP.NET Core’s DI system not being able to resolve generic types more than 1 level deep, I had to come up with something else.

My solution was to make the base validator and pre-processor abstract and generic. That way, the validators and pre-processors for the 2 variants of the request could just inherit the base validator or pre-processor. Like this:

public abstract class MyRequestValidator<T> : AbstractValidator<T> where T : MyRequest {
    // FluentValidation rules defined for the base request
}

public class MyRequest1Validator : MyRequestValidator<MyRequest1> { }
public class MyRequest2Validator : MyRequestValidator<MyRequest2> { }

And the same sort of thing for the pre-processors. This fit into my existing validation pipeline behavior and still let me keep the validation/pre-processor logic centralized.

0reactions
lilasquaredcommented, Apr 21, 2020

@starx207 if you are using constrained generics for pipeline behaviors you can have the validators handle multiple request types

public class MyRequestValidator<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : MyRequest
{
   ...
}

I’m not sure if that works for the pre-processor.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to get different response type for the same request ...
Lets say I have two RequestHandlers. Each handler takes ProductModel as request but returns different type of response.
Read more >
CQRS and MediatR in ASP.NET Core
There are two types of requests in MediatR. One that returns a value, and one that doesn't. Often this corresponds to reads/queries ...
Read more >
AWS Lambda function handler in Java
The RequestHandler interface is a generic type that takes two parameters: the input type and the output type. Both types must be objects....
Read more >
State handlers | Dialogflow CX
These responses are either defined in static agent data or retrieved ... There are two types of state handlers with differing handler requirements: ......
Read more >
Minimal APIs quick reference
... Tutorial: Create a minimal API with ASP.NET Core. The minimal APIs consist of: WebApplication and WebApplicationBuilder · Route Handlers ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found