使用 FluentValidation 验证集合,为 属性 返回一个失败的规则错误
Validate a collection with FluentValidation returning one failed-rule error for a property
我刚开始使用 FluentValidation v9.x,想知道如何验证集合中的规则。
基本上如果我有一个物质集合
public class Substance
{
public int? SubstanceId { get; set; }
public string SubstanceName { get; set; }
public decimal? SubstanceAmount { get; set; }
public int? SubstanceUnitId { get; set; }
public int? SubstanceRouteId { get; set; }
public DateTimeOffset? SubstanceTime { get; set; }
}
我想验证一下:
- 如果 SubstanceAmount 和 SubstanceUnitId 有值,并且
- 如果 SubstanceRouteId 有一个值,并且
- 如果 SubstanceTime 有一个值
在任何物质项目上。
如果规则失败,我希望针对每条规则发回一条错误消息,而不是针对每种物质,这就是现在发生的以下情况:
RuleForEach(x => x.SubstanceList).SetValidator(new SubstanceValidator(RuleSetsToApply));
public class SubstanceValidator : AbstractValidator<Substance>
{
public SubstanceValidator(List<ValidationRule> RuleSetsToApply)
{
string ruleSetName = "SubstanceAmountUnit";
RuleSet(ruleSetName, () => {
RuleFor(x => x.SubstanceAmount).NotNull().NotEmpty();
RuleFor(x => x.SubstanceUnitId).NotNull().NotEmpty().GreaterThan(0);
});
ruleSetName = "SubstanceIngestion";
RuleSet(ruleSetName, () => {
RuleFor(x => x.SubstanceTime).NotNull().NotEmpty();
});
ruleSetName = "SubstanceRoute";
RuleSet(ruleSetName, () => {
RuleFor(x => x.SubstanceRouteId).NotNull().NotEmpty().GreaterThan(0);
});
}
}
所以如果我有五种物质并且
- 第一种物质不符合规则 #2,
- 第三个不符合规则 #1 和 #2 以及
- 第四条不符合规则 #3,
我希望每条规则都返回一个错误,即使规则 #2 失败了两次。
我怎样才能做到这一点?
如果我没有正确理解问题,在这种情况下,我会在父 SubstanceList
而不是 Substance
实体验证器上定义验证规则。
为简洁起见,我没有考虑您的附加规则集逻辑,因为我对此了解不够,也不知道它是否真的需要。但是,以下会产生您拥有的三个验证案例:
- if SubstanceAmount and SubstanceUnitId has values, and
- if SubstanceRouteId has a value, and
- if SubstanceTime has a value on any of the substance items.
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceAmount.HasValue && y.SubstanceUnitId.HasValue && y.SubstanceUnitId.Value <= 0) : true)
.WithMessage(x => "One or more substance amounts or unit ids has not been provided, and/or one or more unit ids is less than or equal to 0.");
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceTime.HasValue) : true)
.WithMessage(x => "One or more substance times has not been provided.");
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceRouteId.HasValue && y.SubstanceRouteId.HasValue && y.SubstanceRouteId.Value <= 0) : true)
.WithMessage(x => "One or more substance route ids has not been provided or is less than or equal to 0.");
根据您的示例场景:
So if I have five substances and
- the first substance fails Rule #2,
- the third fails Rule #1 and #2 and
- the fourth fails on the Rule #3,
I would expect one error for each rule to be returned, even though Rule #2 has failed twice.
当我设置具有以下条件的物质序列时:
- 第一个没有实质时间(规则 #2)
- 第三个没有物质数量(规则#1)或时间(规则#2),并且
- 第四条物质路线 ID <= 0(规则 #3)。
我得到了我认为是期望的输出:
还有其他方法吗?结果是基本上区分错误消息,所以是的,可能还有其他方法。如果您手动调用 Validate
,您会想到的是 post 处理验证结果并确保错误消息是不同的。我更喜欢上面的方法,感觉更确定,也让我有机会提供合适的错误信息。
工作 LINQPad 示例:
void Main()
{
var fixture = new Fixture();
var substances = new List<Substance>();
substances.Add(fixture.Build<Substance>().Without(x => x.SubstanceTime).Create());
substances.Add(fixture.Create<Substance>());
substances.Add(fixture.Build<Substance>().Without(x => x.SubstanceAmount).Without(x => x.SubstanceTime).Create());
substances.Add(fixture.Build<Substance>().With(x => x.SubstanceRouteId, -1).Create());
substances.Add(fixture.Create<Substance>());
Console.WriteLine(substances);
var foo = new Foo() { SubstanceList = substances };
var validator = new FooValidator();
var validationResult = validator.Validate(foo);
Console.WriteLine(validationResult.Errors.Select(x => x.ErrorMessage));
}
public class Substance
{
public int? SubstanceId { get; set; }
public string SubstanceName { get; set; }
public decimal? SubstanceAmount { get; set; }
public int? SubstanceUnitId { get; set; }
public int? SubstanceRouteId { get; set; }
public DateTimeOffset? SubstanceTime { get; set; }
}
public class Foo
{
public List<Substance> SubstanceList { get; set; }
}
public class FooValidator : AbstractValidator<Foo>
{
public FooValidator()
{
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceAmount.HasValue && y.SubstanceUnitId.HasValue && y.SubstanceUnitId.Value <= 0) : true)
.WithMessage(x => "One or more substance amounts or unit ids has not been provided, and/or one or more unit ids is less than or equal to 0.");
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceTime.HasValue) : true)
.WithMessage(x => "One or more substance times has not been provided.");
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceRouteId.HasValue && y.SubstanceRouteId.HasValue && y.SubstanceRouteId.Value <= 0) : true)
.WithMessage(x => "One or more substance route ids has not been provided or is less than or equal to 0.");
}
}
我刚开始使用 FluentValidation v9.x,想知道如何验证集合中的规则。
基本上如果我有一个物质集合
public class Substance
{
public int? SubstanceId { get; set; }
public string SubstanceName { get; set; }
public decimal? SubstanceAmount { get; set; }
public int? SubstanceUnitId { get; set; }
public int? SubstanceRouteId { get; set; }
public DateTimeOffset? SubstanceTime { get; set; }
}
我想验证一下:
- 如果 SubstanceAmount 和 SubstanceUnitId 有值,并且
- 如果 SubstanceRouteId 有一个值,并且
- 如果 SubstanceTime 有一个值 在任何物质项目上。
如果规则失败,我希望针对每条规则发回一条错误消息,而不是针对每种物质,这就是现在发生的以下情况:
RuleForEach(x => x.SubstanceList).SetValidator(new SubstanceValidator(RuleSetsToApply));
public class SubstanceValidator : AbstractValidator<Substance>
{
public SubstanceValidator(List<ValidationRule> RuleSetsToApply)
{
string ruleSetName = "SubstanceAmountUnit";
RuleSet(ruleSetName, () => {
RuleFor(x => x.SubstanceAmount).NotNull().NotEmpty();
RuleFor(x => x.SubstanceUnitId).NotNull().NotEmpty().GreaterThan(0);
});
ruleSetName = "SubstanceIngestion";
RuleSet(ruleSetName, () => {
RuleFor(x => x.SubstanceTime).NotNull().NotEmpty();
});
ruleSetName = "SubstanceRoute";
RuleSet(ruleSetName, () => {
RuleFor(x => x.SubstanceRouteId).NotNull().NotEmpty().GreaterThan(0);
});
}
}
所以如果我有五种物质并且
- 第一种物质不符合规则 #2,
- 第三个不符合规则 #1 和 #2 以及
- 第四条不符合规则 #3,
我希望每条规则都返回一个错误,即使规则 #2 失败了两次。
我怎样才能做到这一点?
如果我没有正确理解问题,在这种情况下,我会在父 SubstanceList
而不是 Substance
实体验证器上定义验证规则。
为简洁起见,我没有考虑您的附加规则集逻辑,因为我对此了解不够,也不知道它是否真的需要。但是,以下会产生您拥有的三个验证案例:
- if SubstanceAmount and SubstanceUnitId has values, and
- if SubstanceRouteId has a value, and
- if SubstanceTime has a value on any of the substance items.
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceAmount.HasValue && y.SubstanceUnitId.HasValue && y.SubstanceUnitId.Value <= 0) : true)
.WithMessage(x => "One or more substance amounts or unit ids has not been provided, and/or one or more unit ids is less than or equal to 0.");
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceTime.HasValue) : true)
.WithMessage(x => "One or more substance times has not been provided.");
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceRouteId.HasValue && y.SubstanceRouteId.HasValue && y.SubstanceRouteId.Value <= 0) : true)
.WithMessage(x => "One or more substance route ids has not been provided or is less than or equal to 0.");
根据您的示例场景:
So if I have five substances and
- the first substance fails Rule #2,
- the third fails Rule #1 and #2 and
- the fourth fails on the Rule #3,
I would expect one error for each rule to be returned, even though Rule #2 has failed twice.
当我设置具有以下条件的物质序列时:
- 第一个没有实质时间(规则 #2)
- 第三个没有物质数量(规则#1)或时间(规则#2),并且
- 第四条物质路线 ID <= 0(规则 #3)。
我得到了我认为是期望的输出:
还有其他方法吗?结果是基本上区分错误消息,所以是的,可能还有其他方法。如果您手动调用 Validate
,您会想到的是 post 处理验证结果并确保错误消息是不同的。我更喜欢上面的方法,感觉更确定,也让我有机会提供合适的错误信息。
工作 LINQPad 示例:
void Main()
{
var fixture = new Fixture();
var substances = new List<Substance>();
substances.Add(fixture.Build<Substance>().Without(x => x.SubstanceTime).Create());
substances.Add(fixture.Create<Substance>());
substances.Add(fixture.Build<Substance>().Without(x => x.SubstanceAmount).Without(x => x.SubstanceTime).Create());
substances.Add(fixture.Build<Substance>().With(x => x.SubstanceRouteId, -1).Create());
substances.Add(fixture.Create<Substance>());
Console.WriteLine(substances);
var foo = new Foo() { SubstanceList = substances };
var validator = new FooValidator();
var validationResult = validator.Validate(foo);
Console.WriteLine(validationResult.Errors.Select(x => x.ErrorMessage));
}
public class Substance
{
public int? SubstanceId { get; set; }
public string SubstanceName { get; set; }
public decimal? SubstanceAmount { get; set; }
public int? SubstanceUnitId { get; set; }
public int? SubstanceRouteId { get; set; }
public DateTimeOffset? SubstanceTime { get; set; }
}
public class Foo
{
public List<Substance> SubstanceList { get; set; }
}
public class FooValidator : AbstractValidator<Foo>
{
public FooValidator()
{
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceAmount.HasValue && y.SubstanceUnitId.HasValue && y.SubstanceUnitId.Value <= 0) : true)
.WithMessage(x => "One or more substance amounts or unit ids has not been provided, and/or one or more unit ids is less than or equal to 0.");
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceTime.HasValue) : true)
.WithMessage(x => "One or more substance times has not been provided.");
RuleFor(x => x.SubstanceList)
.Must(x => x != null ? x.All(y => y.SubstanceRouteId.HasValue && y.SubstanceRouteId.HasValue && y.SubstanceRouteId.Value <= 0) : true)
.WithMessage(x => "One or more substance route ids has not been provided or is less than or equal to 0.");
}
}