在MediaTR验证管道中使用流利的结果以返回结果<TResponse>

问题描述 投票:0回答:4
mediatr

fluent验证 i最初遵循了这篇文章,但没有重新发明我开始使用流利验证管道中的流动性的车轮。基本上,来自我的CQRS查询的所有响应都包裹在结果对象中,这避免了必须处理异常作为错误手持的方法。 ,但是,我无法让我的管道玩得很好:

public class ValidationPipeline<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TResponse : class where TRequest : IRequest<TResponse> { private readonly IValidator<TRequest> _compositeValidator; public ValidationPipeline(IValidator<TRequest> compositeValidator) { _compositeValidator = compositeValidator; } public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) { var result = await _compositeValidator.ValidateAsync(request, cancellationToken); if (!result.IsValid) { Error error = new Error(); var responseType = typeof(TResponse); foreach (var validationFailure in result.Errors) { Log.Warning($"{responseType} - {validationFailure.ErrorMessage}"); error.Reasons.Add(new Error(validationFailure.ErrorMessage)); } // This always returns null instead of a Result with errors in it. var f = Result.Fail(error) as TResponse; return f; } return await next(); } } 我还必须以某种方式将结果对象转换回Tresponse,在其中tresponse始终是结果

任何建议都非常感谢!

Edit:

autofac集成

protected override void Load(ContainerBuilder builder) { var assembly = Assembly.GetExecutingAssembly(); // MediatR builder.AddMediatR(assembly); // Register the Command's Validators (Validators based on FluentValidation library) builder.RegisterAssemblyTypes(assembly) .Where(t => t.IsClosedTypeOf(typeof(IValidator<>))) .AsImplementedInterfaces(); // Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands builder.RegisterAssemblyTypes(assembly) .AsClosedTypesOf(typeof(IRequestHandler<,>)); // Register Behavior Pipeline builder.RegisterGeneric(typeof(ValidationPipeline<,>)).As(typeof(IPipelineBehavior<,>)); }

您应该更改 there tresponse:class

to
there tresponse:结果
c# asp.net-core cqrs fluentvalidation mediatr
4个回答
3
投票
确保您的所有请求均为iirequest

the t是您要返回的实际响应。

我正在编辑我的帖子,但是我认为这几乎是我问题的答案。

我已经能够将问题隔离到以下行:

var f = Result.Fail(error).ToResult<CustomClass>() as TResponse; return f;

如果我很难将我传递给结果的课程,那么转换就可以按预期工作,一切都应根据。现在的问题变成了,如何获得可以从tresponse中传递到.ToResult<T>()

的类参考?

我一直在寻找答案,但是从根本上讲,在运行时,我在编译时需要它需要它的时间类参考是不可能的。

0
投票

var resultType = typeof(TResponse).GetGenericArguments()[0]; var invalidResponseType = typeof(ValidateableResponse<>).MakeGenericType(resultType); var f = Activator.CreateInstance(invalidResponseType, null) as TResponse; return f;

这将起作用,但是结果对象具有封闭的构造函数,因此我有一个例外。我已经在FluentResult的Github上留下了一个问题,也许可以更改。

现在我有一个解决方法,我让我的处理程序从basehandler
继承
BaseHandler:

public class BaseHandler
{
    public Result Validate<TQuery, TValidator>(TQuery request) where TValidator : AbstractValidator<TQuery>
    {
        var validator = (TValidator)Activator.CreateInstance(typeof(TValidator));
        var result = validator.Validate(request);
        return CreateResult(result);
    }

    public async Task<Result> ValidateAsync<TQuery, TValidator>(TQuery request) where TValidator : AbstractValidator<TQuery>
    {
        var validator = (TValidator)Activator.CreateInstance(typeof(TValidator));
        var result = await validator.ValidateAsync(request);
        return CreateResult(result);
    }

    private Result CreateResult(ValidationResult result)
    {
        if (!result.IsValid)
        {
            if (result.Errors.Count == 1)
            {
                string msg = $"Validation Failure: {result.Errors.First().ErrorMessage}";
                return Result.Fail(msg);
            }

            Error error = new Error("Validation Failure");

            foreach (var validationFailure in result.Errors)
            {
                Log.Warning($"{validationFailure.ErrorMessage}");
                error.Reasons.Add(new Error(validationFailure.ErrorMessage));
            }
            return Result.Fail(error);
        }
        return Result.Ok();
    }
}
usage示例:

// At the top of my handle function var result = await ValidateAsync<GetItemByIdQuery, GetItemByIdQueryValidator>(request); if (result.IsFailed) return result;

这也起作用,唯一的缺点是我必须在每个句柄功能的顶部添加它才能启用验证。

现在很好,我将拭目以待,看看是否可以更新FluentResult软件包,以便我可以尝试较早的建议。
感谢所有人的建议!
    

如果我理解您在问什么,我认为我做了与您要做的同一件事。以下是我解决这个问题的解决方案。

public class ValidationPipeline<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TResponse : ResultBase<TResponse>, new() where TRequest : IRequest<TResponse> { private readonly IEnumerable<IValidator<TRequest>> _validators; public ValidationPipeline(IEnumerable<IValidator<TRequest>> validators) { _validators = validators ?? throw new ArgumentNullException(nameof(validators)); } public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) { var validationFailures = _validators.Select(validator => validator.Validate(request)) .SelectMany(validationResult => validationResult.Errors) .Where(validationFailure => validationFailure != null).ToList(); if (validationFailures.Any()) { var responseType = typeof(TResponse); TResponse invalidResponse; if (responseType.IsGenericType) { var resultType = responseType.GetGenericArguments()[0]; var invalidResponseType = typeof(Result<>).MakeGenericType(resultType); invalidResponse = Activator.CreateInstance(invalidResponseType, null) as TResponse; } else { invalidResponse = new TResponse(); } invalidResponse.WithErrors(validationFailures.Select(s => s.ErrorMessage)); return invalidResponse; } return await next(); } }

this将处理您使用结果或结果的情况。

我也有类似的问题。此外,有些处理程序返回了
Result

,而其他处理程序却是

Result<ABC>
Result<XYZ>

。通用行为无法处理这一点。

两个解决方法:

归还返回的

0
投票
将异常放在管道下(捕获或全局错误句柄)。

像此一样,分别注册每个处理程序和管道

Result


0
投票
但是这很头疼,必须自动化。

    
也许有点晚了,但这是我所有请求都返回结果或结果的管道解决方案。
services.AddTransient( typeof(IPipelineBehavior<MyCommand,Result<MyCommandResult>>), typeof(MyBehavior<MyCommand, MyCommandResult>));
我使用的默认错误。

public class FluentValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse> where TResponse : ResultBase<TResponse>, new() { private readonly IEnumerable<IValidator<TRequest>> _validators; public FluentValidationBehaviour(IEnumerable<IValidator<TRequest>> validators) { _validators = validators; } public async Task<TResponse> Handle( TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken) { if (!_validators.Any()) return await next(); var context = new ValidationContext<TRequest>(request); var validationResults = await Task.WhenAll(_validators.Select(validator => validator.ValidateAsync(context, cancellationToken))); var failures = validationResults .SelectMany(result => result.Errors) .GroupBy(error => error.PropertyName) .ToDictionary( group => group.Key, group => group.Select(error => error.ErrorMessage) ); if (failures.Count != 0) return new TResponse() .WithError(new BadRequestError("Erro de validação", failures)); return await next(); } }

Request示例

    public abstract class ApiError<TExtension> : Error { protected ApiError( HttpStatusCode statusCode, string? detail = null, TExtension? extensions = default, ResultErrorExtensions extensionsType = ResultErrorExtensions.Errors) : base(detail) { Metadata.Add("Title", statusCode); Metadata.Add("StatusCode", (int)statusCode); if (extensions != null) Metadata.Add(extensionsType.ToString(), extensions); } } public sealed class BadRequestError(string detail, object? errors = null) : ApiError<object>(HttpStatusCode.BadRequest, detail, errors);
  1. 
    
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.