基于其他属性验证属性的最佳实践
Best practice to validate properties based on other properties
假设您有一个 class 像 :
public enum Kind { Kind1, Kind2 }
public class MyForm
{
public string Kind { get; set; }
public ACustomClass1 Custom1 { get; set; }
public ACustomClass2 Custom2 { get; set; }
}
并且您想在 Kind == Kind1
时用 Custom1Validator
验证 Custom1
(显然 Kind == Kind2
时 Custom2
和 Custom2Validator
)
继续使用 8.6.0 版的最佳方法是什么?
目前,我是这样做的(但我觉得很别扭):
public class MyFormValidator : AbstractValidator<MyForm>
{
public MyFormValidator (IStringLocalizer<Strings> localizer, Custom1Validator validator1, Custom2Validator validator2)
{
//validate Kind and then, in function of Kind, use correct validator
RuleFor(x => x).Custom((f, context) => {
if (!Enum.TryParse<Kind>(f.Kind, out var kind))
{
context.AddFailure(localizer["Invalid Kind"]);
return;
}
switch (kind)
{
case Kind.Kind1:
if (f.Custom1 == null)
{
context.AddFailure(localizer["Invalid Kind"]);
}
else if (! validator1.Validate(f.Custom1, out var firstError))
{
context.AddFailure(firstError);
}
break;
case Kind.Kind2:
if (f.Custom2 == null)
{
context.AddFailure(localizer["Invalid Kind"]);
}
else if (!validator2.Validate(f.Custom2, out var firstError))
{
context.AddFailure(firstError);
}
break;
}
});
}
}
请注意,我正在使用 asp.net 内核和依赖注入(这就是为什么有 IStringLocalizer
而我不能对 Custom1 和 Custom2 使用 SetValidator
)
我想要的是
RuleFor(x => x.Kind).NotEmpty().IsEnumName(typeof(Kind)).withMessage(_ => localizer["Invalid Kind"]);
RuleFor(x => x.Custom1).NotEmptyWhen(f => f.Kind == Kind.Custom1.ToString()).withMessage(_ => localizer["Invalid Kind"])
RuleFor(x => x.Custom1).SetValidator(validator1); //would be executed only when custom1 is not null
//自定义2相同
问题是我不知道如何编写 NotEmptyWhen
方法
重组?
根据您发布的代码片段的外观,我认为 MyForm
永远不会在同一请求中填充 Custom1
和 Custom2
属性。因此,我鼓励您直接使用代表正在验证的有效载荷的模型,而不是拥有一个包含两个有效载荷 种类 的父模型。这样你就不会 运行 进入这种在必要时检查 种类 的讨厌模式。
您的一个表单端点接受一个 Custom1
,它有一个关联的 Custom1Validator
。您的另一个表单端点接受一个 Custom2
,它有一个关联的 Custom2Validator
。它们是解耦的。您可以安全地更改一个而不影响另一个。
使用 Fluent 验证条件 (When/Unless)
如果您执意要让 一个 模型负责表示多个请求的负载(请不要),您可以使用 When()
方法由图书馆提供。查看他们关于 conditional rules.
的文档
假设您有一个 class 像 :
public enum Kind { Kind1, Kind2 }
public class MyForm
{
public string Kind { get; set; }
public ACustomClass1 Custom1 { get; set; }
public ACustomClass2 Custom2 { get; set; }
}
并且您想在 Kind == Kind1
时用 Custom1Validator
验证 Custom1
(显然 Kind == Kind2
时 Custom2
和 Custom2Validator
)
继续使用 8.6.0 版的最佳方法是什么?
目前,我是这样做的(但我觉得很别扭):
public class MyFormValidator : AbstractValidator<MyForm>
{
public MyFormValidator (IStringLocalizer<Strings> localizer, Custom1Validator validator1, Custom2Validator validator2)
{
//validate Kind and then, in function of Kind, use correct validator
RuleFor(x => x).Custom((f, context) => {
if (!Enum.TryParse<Kind>(f.Kind, out var kind))
{
context.AddFailure(localizer["Invalid Kind"]);
return;
}
switch (kind)
{
case Kind.Kind1:
if (f.Custom1 == null)
{
context.AddFailure(localizer["Invalid Kind"]);
}
else if (! validator1.Validate(f.Custom1, out var firstError))
{
context.AddFailure(firstError);
}
break;
case Kind.Kind2:
if (f.Custom2 == null)
{
context.AddFailure(localizer["Invalid Kind"]);
}
else if (!validator2.Validate(f.Custom2, out var firstError))
{
context.AddFailure(firstError);
}
break;
}
});
}
}
请注意,我正在使用 asp.net 内核和依赖注入(这就是为什么有 IStringLocalizer
而我不能对 Custom1 和 Custom2 使用 SetValidator
)
我想要的是
RuleFor(x => x.Kind).NotEmpty().IsEnumName(typeof(Kind)).withMessage(_ => localizer["Invalid Kind"]);
RuleFor(x => x.Custom1).NotEmptyWhen(f => f.Kind == Kind.Custom1.ToString()).withMessage(_ => localizer["Invalid Kind"])
RuleFor(x => x.Custom1).SetValidator(validator1); //would be executed only when custom1 is not null
//自定义2相同
问题是我不知道如何编写 NotEmptyWhen
方法
重组?
根据您发布的代码片段的外观,我认为 MyForm
永远不会在同一请求中填充 Custom1
和 Custom2
属性。因此,我鼓励您直接使用代表正在验证的有效载荷的模型,而不是拥有一个包含两个有效载荷 种类 的父模型。这样你就不会 运行 进入这种在必要时检查 种类 的讨厌模式。
您的一个表单端点接受一个 Custom1
,它有一个关联的 Custom1Validator
。您的另一个表单端点接受一个 Custom2
,它有一个关联的 Custom2Validator
。它们是解耦的。您可以安全地更改一个而不影响另一个。
使用 Fluent 验证条件 (When/Unless)
如果您执意要让 一个 模型负责表示多个请求的负载(请不要),您可以使用 When()
方法由图书馆提供。查看他们关于 conditional rules.