我正在使用FluentValidation,我希望使用某些对象的属性值格式化消息。问题是我对C#中的表达式和委托的经验很少。
FluentValidation已经提供了一种使用格式参数执行此操作的方法。
RuleFor(x => x.Name).NotEmpty()
.WithMessage("The name {1} is not valid for Id {0}", x => x.Id, x => x.Name);
我想做这样的事情,以避免在我更改参数顺序时更改消息字符串。
RuleFor(x => x.Name).NotEmpty()
.WithMessage("The name {Name} is not valid for Id {Id}",
x => new
{
Id = x.Id,
Name = x.Name
});
原始方法签名如下所示:
public static IRuleBuilderOptions<T, TProperty> WithMessage<T, TProperty>(
this IRuleBuilderOptions<T, TProperty> rule, string errorMessage,
params Func<T, object>[] funcs)
我在考虑为这个方法提供一个Func列表。
任何人都可以帮我这个吗?
答案 0 :(得分:16)
使用C#6.0,这大大简化了。现在你可以做到这一点(有点像黑客,但比提供流利验证要好得多):
RuleFor(x => x.Name).NotEmpty()
.WithMessage("{0}", x => $"The name {x.Name} is not valid for Id {x.Id}.");
可惜他们没有提供WithMessage
重载,需要lambda接受该对象,你可以这样做:
RuleFor(x => x.Name).NotEmpty()
.WithMessage(x => $"The name {x.Name} is not valid for Id {x.Id}.");
我认为愚蠢的是他们试图在目标中复制string.Format
以实现更短的语法,但最终使其灵活性降低,因此我们无法干净地使用新的C#6.0语法。
答案 1 :(得分:10)
您不能使用FluentValidation中的WithMessage执行此操作,但您可以高举JackState属性并在那里注入您的消息。这是一个有效的例子;您的另一个选择是fork FluentValidation并为WithMethod进行额外的重载。
这是一个控制台应用程序,在此博客文章中引用了Nuget和JamesFormater的FluentValidation:
http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx
最佳答案。从Ilya那里获得灵感,并意识到你可以捎带流畅验证的扩展方法本质。所以下面的工作无需修改库中的任何内容。
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);
}
}
}
}
答案 2 :(得分:9)
虽然KhalidAbuhakmeh的答案非常好而且很深,但我只想分享一个解决这个问题的简单方法。如果您害怕位置参数,为什么不用连接运算符+
封装错误创建机制并利用WithMessage
重载,这需要Func<T, object>
。这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;
}
}
在下一个代码段中打印正确的原始错误消息:
var customer = new Customer() {Id = 1, Name = ""};
var result = new CustomerValidator().Validate(customer);
Console.WriteLine(result.Errors.First().ErrorMessage);
或者,使用内联lambda:
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);
}
}
答案 3 :(得分:2)
对于现在正在研究此问题的任何人-当前的FluentValidation(v8.0.100)允许您在WithMessage中使用lamda(如上面的ErikE建议),因此您可以使用:
RuleFor(x => x.Name).NotEmpty()
.WithMessage(x => $"The name {x.Name} is not valid for Id {x.Id}.");
希望这对某人有帮助。
答案 4 :(得分:1)
基于ErikE answer的扩展方法。
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);
}
用法示例:
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}.");