$mermaidjs
Clean Architecture Demo
Loading...
Searching...
No Matches
ValidationBehavior.cs
Go to the documentation of this file.
1// TaskManagement.Application/Behaviors/ValidationBehavior.cs
2using FluentValidation;
3using MediatR;
6
8
60public sealed class ValidationBehavior<TRequest, TResponse>
61 : IPipelineBehavior<TRequest, TResponse>
62 where TRequest : IRequest<TResponse>
63 where TResponse : Result
64{
65 private readonly IEnumerable<IValidator<TRequest>> _validators;
66 public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
67 {
68 _validators = validators;
69 }
70
71 public async Task<TResponse> Handle(
72 TRequest request,
73 RequestHandlerDelegate<TResponse> next,
74 CancellationToken cancellationToken)
75 {
76 ArgumentNullException.ThrowIfNull(next);
77
78 if (!_validators.Any())
79 return await next(cancellationToken).ConfigureAwait(false);
80
81 var context = new ValidationContext<TRequest>(request);
82 var validationResults = await Task.WhenAll(
83 _validators.Select(
84 v => v.ValidateAsync(context, cancellationToken))).ConfigureAwait(false);
85 var failures = validationResults
86 .SelectMany(r => r.Errors)
87 .Where(f => f != null)
88 .Select(f => f.ErrorMessage)
89 .ToList();
90
91 if (failures.Count > 0)
92 {
93 // Utilizar reflexión para crear resultado de fracaso de tipo genérico correcto
94 return CreateFailureResult(failures);
95 }
96
97 return await next(cancellationToken).ConfigureAwait(false);
98 }
99
100 private static TResponse CreateFailureResult(List<string> errors)
101 {
102 ArgumentNullException.ThrowIfNull(errors);
103
104 var responseType = typeof(TResponse);
105
106 if (responseType.IsGenericType
107 && responseType.GetGenericTypeDefinition() == typeof(Result<>))
108 {
109 var genericArgument = responseType.GenericTypeArguments[0];
110 var failureMethod = typeof(Result).GetMethods()
111 .First(m =>
112 m.Name == nameof(Result.Failure)
113 && m.IsGenericMethodDefinition
114 && m.GetParameters().Length == 1
115 && m.GetParameters()[0].ParameterType == typeof(IEnumerable<string>));
116
117 return (TResponse)failureMethod.MakeGenericMethod(genericArgument)
118 .Invoke(null, new object[] { errors })!;
119 }
120
121 return (TResponse)(object)Result.Failure(errors);
122 }
123}
124
125// FluentValidation validator
126public sealed class CreateTaskCommandValidator : AbstractValidator<CreateTaskCommand>
127{
129 {
130 RuleFor(x => x.Title)
131 .NotEmpty().WithMessage("Title is required.")
132 .MaximumLength(200).WithMessage("Title cannot exceed 200 characters.");
133 RuleFor(x => x.Description)
134 .MaximumLength(2000).WithMessage("Description cannot exceed 2000 characters.");
135 RuleFor(x => x.DueDate)
136 .Must(date => !date.HasValue || date.Value >= DateTime.UtcNow.Date)
137 .WithMessage("Due date cannot be in the past.");
138 RuleFor(x => x.CreatedBy)
139 .NotEmpty().WithMessage("CreatedBy is required.");
140 }
141}
builder.Services. typeof(ValidationBehavior<,>)) .AddDbContext< TaskDbContext >(options
ValidationBehavior es un comportamiento de pipeline de MediatR para validar comandos y consultas.
ValidationBehavior(IEnumerable< IValidator< TRequest > > validators)
async Task< TResponse > Handle(TRequest request, RequestHandlerDelegate< TResponse > next, CancellationToken cancellationToken)
Result es un tipo de unión discriminada que implementa el patrón Result para manejo de errores.
Definition Result.cs:71
static Result Failure(string error)