c# - net - fluentvalidation web api core
Uso del método WithMessage de FluentValidation con una lista de parámetros nombrados (5)
Con C # 6.0, esto se simplifica enormemente. Ahora puedes hacer esto (un poco de pirateo, pero mucho mejor que forking Validar Validación):
RuleFor(x => x.Name).NotEmpty()
.WithMessage("{0}", x => $"The name {x.Name} is not valid for Id {x.Id}.");
Lástima que no hayan ofrecido una sobrecarga de WithMessage
que requiera un lambda que acepte el objeto, y usted podría simplemente hacer:
RuleFor(x => x.Name).NotEmpty()
.WithMessage(x => $"The name {x.Name} is not valid for Id {x.Id}.");
Creo que es una tontería que intentaran duplicar string.Format
el objetivo de lograr una sintaxis más corta, pero en última instancia la hicieron menos flexible para que no podamos usar la nueva sintaxis de C # 6.0 de manera limpia.
Estoy usando FluentValidation y quiero formatear un mensaje con algunas de las propiedades del objeto. El problema es que tengo muy poca experiencia con expresiones y delegados en C #.
FluentValidation ya proporciona una forma de hacer esto con argumentos de formato.
RuleFor(x => x.Name).NotEmpty()
.WithMessage("The name {1} is not valid for Id {0}", x => x.Id, x => x.Name);
Me gustaría hacer algo como esto para evitar tener que cambiar la cadena del mensaje si cambio el orden de los parámetros.
RuleFor(x => x.Name).NotEmpty()
.WithMessage("The name {Name} is not valid for Id {Id}",
x => new
{
Id = x.Id,
Name = x.Name
});
La firma del método original se ve así:
public static IRuleBuilderOptions<T, TProperty> WithMessage<T, TProperty>(
this IRuleBuilderOptions<T, TProperty> rule, string errorMessage,
params Func<T, object>[] funcs)
Estaba pensando en proporcionar este método con una lista de funciones.
¿Alguien me puede ayudar con esto?
Métodos de extensión basados en la answer de ErikE.
public static class RuleBuilderOptionsExtensions
{
public static IRuleBuilderOptions<T, TProperty> WithMessage<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule, Func<T, object> func)
=> DefaultValidatorOptions.WithMessage(rule, "{0}", func);
public static IRuleBuilderOptions<T, TProperty> WithMessage<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule, Func<T, TProperty, object> func)
=> DefaultValidatorOptions.WithMessage(rule, "{0}", func);
}
Ejemplos de uso:
RuleFor(_ => _.Name).NotEmpty()
.WithMessage(_ => $"The name {_.Name} is not valid for Id {_.Id}.");
RuleFor(_ => _.Value).GreaterThan(0)
.WithMessage((_, p) => $"The value {p} is not valid for Id {_.Id}.");
No puede hacer eso con WithMessage en FluentValidation, pero puede robar la propiedad CustomState e inyectar su mensaje allí. Aquí hay un ejemplo de trabajo; Su otra opción es bifurcar FluentValidation y hacer 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. Tomé la inspiración de Ilya y me di cuenta de que puedes usar el método de extensión de la validación fluida. Así que lo de abajo 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 .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);
}
}
}
}
Para cualquier persona que esté investigando esto ahora, la actual FluentValidation (v8.0.100) le permite usar un lamda en WithMessage (como ErikE sugirió anteriormente) para que pueda usar:
RuleFor(x => x.Name).NotEmpty()
.WithMessage(x => $"The name {x.Name} is not valid for Id {x.Id}.");
Espero que esto ayude a alguien.
Si bien la respuesta de KhalidAbuhakmeh es muy buena y profunda, solo quiero compartir una solución simple a este problema. Si tiene miedo de los argumentos posicionales, ¿por qué no encapsular el mecanismo de creación de errores con el operador de concatenación +
y aprovechar la sobrecarga de WithMessage
, que toma Func<T, object>
. Este 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 un 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);
}
}