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);
}
}