Saltar al contenido

Usando el método WithMessage de FluentValidation con una lista de parámetros con nombre

Solución:

Si está utilizando C # 6.0 o posterior, aquí hay una sintaxis mejorada.

Con la versión 8.0.100 o posterior de Fluent Validation, hay una WithMessage sobrecarga que toma una lambda que acepta el objeto, y puede hacer:

RuleFor(x => x.Name)
   .NotEmpty()
   .WithMessage(x => $"The name {x.Name} is not valid for Id {x.Id}.");

Sin embargo, con las versiones anteriores de Fluent Validation, esta forma algo hacky sigue siendo bastante limpia y mucho mejor que bifurcar sus versiones anteriores:

RuleFor(x => x.Name)
   .NotEmpty()
   .WithMessage("{0}", x => $"The name {x.Name} is not valid for Id {x.Id}.");

No puede hacer eso con WithMessage en FluentValidation, pero puede conectar la propiedad CustomState e inyectar su mensaje allí. Aquí hay un ejemplo práctico; Su otra opción es bifurcar FluentValidation y realizar una sobrecarga adicional para WithMethod.

Esta es una aplicación de consola con referencias a FluentValidation de Nuget y JamesFormater de esta publicación de blog:

http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx

La mejor respuesta. Se inspiró en Ilya y se dio cuenta de que puede aprovechar la naturaleza del método de extensión de la validación fluida. Entonces, lo siguiente funciona sin necesidad de modificar nada en la biblioteca.

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using FluentValidation;

namespace stackoverflow.fv
{
    class Program
    {
        static void Main(string[] args)
        {
            var target = new My() { Id = "1", Name = "" };
            var validator = new MyValidator();
            var result = validator.Validate(target);

            foreach (var error in result.Errors)
                Console.WriteLine(error.ErrorMessage);

            Console.ReadLine();
        }
    }

    public class MyValidator : AbstractValidator<My>
    {
        public MyValidator()
        {
            RuleFor(x => x.Name).NotEmpty().WithNamedMessage("The name {Name} is not valid for Id {Id}");
        }
    }

    public static class NamedMessageExtensions
    {
        public static IRuleBuilderOptions<T, TProperty> WithNamedMessage<T, TProperty>(
            this IRuleBuilderOptions<T, TProperty> rule, string format)
        {
            return rule.WithMessage("{0}", x => format.JamesFormat(x));
        }
    }

    public class My
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }

    public static class JamesFormatter
    {
        public static string JamesFormat(this string format, object source)
        {
            return FormatWith(format, null, source);
        }

        public static string FormatWith(this string format
            , IFormatProvider provider, object source)
        {
            if (format == null)
                throw new ArgumentNullException("format");

            List<object> values = new List<object>();
            string rewrittenFormat = Regex.Replace(format,
              @"(?<start>{)+(?<property>[w.[]]+)(?<format>:[^}]+)?(?<end>})+",
              delegate(Match m)
              {
                  Group startGroup = m.Groups["start"];
                  Group propertyGroup = m.Groups["property"];
                  Group formatGroup = m.Groups["format"];
                  Group endGroup = m.Groups["end"];

                  values.Add((propertyGroup.Value == "0")
                    ? source
                    : Eval(source, propertyGroup.Value));

                  int openings = startGroup.Captures.Count;
                  int closings = endGroup.Captures.Count;

                  return openings > closings || openings % 2 == 0
                     ? m.Value
                     : new string('{', openings) + (values.Count - 1)
                       + formatGroup.Value
                       + new string('}', closings);
              },
              RegexOptions.Compiled
              | RegexOptions.CultureInvariant
              | RegexOptions.IgnoreCase);

            return string.Format(provider, rewrittenFormat, values.ToArray());
        }

        private static object Eval(object source, string expression)
        {
            try
            {
                return DataBinder.Eval(source, expression);
            }
            catch (HttpException e)
            {
                throw new FormatException(null, e);
            }
        }
    }
}

Si bien la respuesta de KhalidAbuhakmeh es muy buena y profunda, solo quiero compartir una solución simple a este problema. Si le temen a los argumentos posicionales, ¿por qué no encapsular el mecanismo de creación de errores con el operador de concatenación? + y aprovechar WithMessage sobrecarga, eso lleva Func<T, object>. Esta CustomerValudator

public class CustomerValidator : AbstractValidator<Customer>
{
    public CustomerValidator()
    {
        RuleFor(customer => customer.Name).NotEmpty().WithMessage("{0}", CreateErrorMessage);
    }

    private string CreateErrorMessage(Customer c)
    {
        return "The name " + c.Name + " is not valid for Id " + c.Id;
    }
}

Imprime el mensaje de error original correcto en el siguiente fragmento de código:

var customer = new Customer() {Id = 1, Name = ""};
var result = new CustomerValidator().Validate(customer);

Console.WriteLine(result.Errors.First().ErrorMessage);

Alternativamente, use una lambda en línea:

public class CustomerValidator : AbstractValidator<Customer>
{
    public CustomerValidator()
    {
        RuleFor(customer => customer.Name)
            .NotEmpty()
            .WithMessage("{0}", c => "The name " + c.Name + " is not valid for Id " + c.Id);
    }
}
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *