Returning Result<T> from a behaviour.
See original GitHub issueMediatR version: 9.0.0
.NET target framework: net5.0
dotnet version: 5.0.201
I’m trying to implement FluentValidation without throwing an exception, and to do that I’m using a Result<T> type. I have the following Query:
public class TestQuery
{
public record Query(string? TestString) : IRequest<Result<string>>;
public class QueryValidator : AbstractValidator<Query>
{
public QueryValidator()
{
RuleFor(x => x.TestString)
.NotEmpty();
}
}
public class Handler : RequestHandler<Query, Result<string>>
{
protected override Result<string> Handle(Query request)
{
return request.TestString!;
}
}
and the behaviour written like so:
public class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, Result<TResponse>>
where TRequest : IRequest<Result<TResponse>>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehaviour(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public Task<Result<TResponse>> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<Result<TResponse>> next)
{
var context = new ValidationContext<TRequest>(request);
var failures = _validators
.Select(x => x.Validate(context))
.SelectMany(x => x.Errors)
.Where(x => x is not null)
.ToList();
if (failures.Any())
{
return Task.FromResult(Result<TResponse>.FromError(new GenericError("test")));
}
return next();
}
}
and I register the behaviour with DI like so:
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>));
However the behaviour never runs when I run the query (I have verified with the debugger). What gives?
Issue Analytics
- State:
- Created 2 years ago
- Comments:13 (2 by maintainers)
Top Results From Across the Web
1.4 "return result" behavior question
When the return result; statement is inside of the switch(movie) {} sequence, then there is no function to determine what result should be...
Read more >c++ - Function with missing return value, behavior at runtime
Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning...
Read more >What's your return on behaviour?
I explained “There are three kinds of behavior - productive, denying and unproductive.” “We're going to focus on the unproductive behaviours ...
Read more >Using the values returned by behaviour formulas (ie Patch)
Solved: How can we read/use the values returned by behaviour formulas such as Patch? The reference article mentions this: " The return value...
Read more >Retaining or returning? Some insights for a better ...
Some insights for a better understanding of return behaviour ... This paper details an empirical study of return behaviour based on a field ......
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Is there no way around this aside from reflection? How do people normally handle validation errors without throwing exceptions?
I think the issue is right here: https://github.com/jbogard/MediatR/blob/50af3de7fa89d56067952581179fa22a1b66de0b/src/MediatR/Wrappers/RequestHandlerWrapper.cs#L63
When mediatr is trying to locate your behaviours, it’s asking whichever DI adapter you’re using for the set of IPipelineBehavior<TRequest,TResponse> implementations it has registered. In this case, in RequestHandlerWrapperImpl, TRequest will be of type Query and TResponse will be of type
Result<string>
. This is an issue, because when RequestHandlerWrapperImpl asks DI for an IBehavior, it’s going to try to instantiate ValidationBehaviour using the generic parameters<Query, Result<string>>
and not the<Query,string>
you were probably expecting it to ask for. If you turn on a first chance exception handler or look in the visual studio exception timeline you’ll see an exception that looks like this (I changed the names of TRequest and TResponse in your behavior to TReq and TResp to differentiate them from the names in RequestHandlerWrapperImpl):I’m guessing your behavior registration looked like this (assuming you’re using the microsoft DI container):
services.AddTransient(typeof(IPipelineBehavior<,>),typeof(ValidationBehaviour<,>));
If you change it to the following, you’ll probably see it start working:
services.AddTransient(typeof(IPipelineBehavior<Query,Result<string>>), typeof(ValidationBehaviour<Query,string>));
If you want to not have to manually register every possible
Result<T> => ValidationBehavior<?,T>
combination, I think you’re going to have to resort to reflection (what I did), or find a container that does the work of creating those closed implementation types for you. If there’s another way to do it, I’d love to hear it. I’m fairly sure I remember seeing a bug in here a couple of years back that had some gnarly reflection code to accomplish this - I’ll update if I can find it.Edit: There are probably a dozen bugs here that are all asking variations of this question (just search for IPipelineBehavior). Most are in the simple case (TResponse isn’t generic) that’s been solved in .net 5.0 now that open generic registration is allowed, but quite a few are in your (and my) category where TResponse is generic. Here are a few of the latter case: https://github.com/jbogard/MediatR/issues/562 https://github.com/jbogard/MediatR/issues/583 https://github.com/jbogard/MediatR/issues/466 https://github.com/jbogard/MediatR/issues/305 https://github.com/jbogard/MediatR/issues/200