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.

Issue with Generic Wrapper as response type in pipeline

See original GitHub issue

When adding a Generic request result wrapper to all of my mediator request response types, the pipeline refuses to execute, throwing a generic type constraint exception that the request does not fit the type constraint of TRequest, which is where TRequest : IRequest<RequestResponse<TResponse>> The request sent is of type IRequest<RequestResponse<Detail>>

The exact exception is Message "GenericArguments[0], 'Api.MediatR.Requests.GetDetailsRequest', on 'Api.MediatR.Behaviours.ExceptionHandlingBehaviour2[TRequest,TResponse]' violates the constraint of type parameter 'TRequest'." string

The full class in question is

public class ExceptionHandlingBehaviour<TRequest, TResponse> : 
 IPipelineBehavior<TRequest,RequestResponse<TResponse>>
        where TRequest : IRequest<RequestResponse<TResponse>> 
    {
        public async Task<RequestResponse<TResponse>> Handle(TRequest request, RequestHandlerDelegate<RequestResponse<TResponse>> next)
        {
            try
            {
                return await next();
            }
            catch (Exception ex)
            {
                return new RequestResponse<TResponse>(ex);
            }
        }
    }

With RequestResult being

public class RequestResponse<TResponse>
    {
        public RequestResponse(RequestState reason, string message = null)
        {
            RequestState = reason;
            Failure = message != null ? new Exception(message) : new Exception(reason.ToString());
        }
        public RequestResponse(List<ValidationFailure> validationFailures)
        {
            ValidationFailures = validationFailures;
            RequestState = RequestState.ValidationFailure;
        }

        public RequestResponse(TResponse response)
        {
            RequestState = RequestState.Success;
            Response = response;
        }

        public RequestResponse(Exception exception)
        {
            Failure = exception;
            RequestState = RequestState.Error;
        }

        public readonly TResponse Response;
        public readonly IEnumerable<ValidationFailure> ValidationFailures;

        public readonly Exception Failure;

        public RequestState RequestState;
    }

Is this just me stuffing up generics, or is something not working correctly here?

Issue Analytics

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

github_iconTop GitHub Comments

5reactions
lilasquaredcommented, Dec 11, 2018

@stumpykilo let me know if this is what you are looking for.

public async Task<TResponse> Handle(TCommand request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
    try
    {
        return await next().ConfigureAwait(false);
    }
    catch (Exception ex)
    {
        var responseType = typeof(TResponse);

        if (responseType.IsGenericType)
        {
            var resultType = responseType.GetGenericArguments()[0];
            var response = typeof(RequestResponse<>).MakeGenericType(resultType);
            return Activator.CreateInstance(resultType, ex) as TResponse;
        }

        return new RequestResponse(ex) as TResponse;
    }
}

This is close to what we do in our project.

2reactions
lilasquaredcommented, Jul 15, 2017

@jakerabjohns15 check out these lines

public class ExceptionHandlingBehaviour<TRequest, TResponse> : 
 IPipelineBehavior<TRequest,RequestResponse<TResponse>>
        where TRequest : IRequest<RequestResponse<TResponse>> 

what you are saying here is that ExceptionHandlingBehaviour is a generic class that accepts two types, TRequest and TResponse.

This class also implements a Generic interface that accepts two generic types TRequest (which is the same as the generic from ExceptionHandlingBehaviour and RequestResponse<TResponse>. Here TResponse is also carried over from ExceptionHandlingBehaviour. Are you seeing the problem?

If TRequest is constrained to types of IRequest<RequestResponse<TResponse>> that means that TResponse is intended through mediatr’s rules to be of type RequestResponse<TResponse>.

This would imply that TResponse itself is of type RequestResponse<TResponse> which would imply that the pipeline to be limited to IPipelineBehavior<TRequest, RequestResponse<RequestResponse<TResponse>>>. You can see how this might be a problem.

To do what you need you cannot reference the TResponse twice. You will need to encapsulate the Exception part into a parent class.

    public class RequestResponse
    {
        public Exception Failure { get; }

        public RequestResponse(Exception ex)
        {
            Failure = ex;
        }
    }

    public class RequestResponse<T> : RequestResponse
    {
        public RequestResponse(T response, Exception ex = null) : base(ex)
        {
            Response = response;
        }

        public T Response { get; }
    }

And then modify your pipeline handler like so:

public class ExceptionHandlingBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> 
        where TResponse : RequestResponse

This should give you the desired behavior

Read more comments on GitHub >

github_iconTop Results From Across the Web

Constraint Violated Exception on MediatR Behavior
This means that in your generic implementation, TRequest will be Request , and TResponse will be Envelope<Response> . This however means that ...
Read more >
How to wrap a route not found exception with the generic ...
I am trying to capture the exception when the route is not found and wrap the exception with the generic response model.
Read more >
Creating generic networking APIs in Swift
One option would be to wrap the model that we're looking to extract into a dedicated response type that we can then decode...
Read more >
Understanding TypeScript Generics
An introduction to the utilization of Generics in TypeScript with examples grounded in real-world use cases, such as collections, ...
Read more >
MediatR Pipeline Examples
My authorization handler applies the former based on generic type (TRequest, TResponse) and the latter based on an attribute I add to my...
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