基于其他属性验证属性的最佳实践

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 == Kind2Custom2Custom2Validator

继续使用 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 永远不会在同一请求中填充 Custom1Custom2 属性。因此,我鼓励您直接使用代表正在验证的有效载荷的模型,而不是拥有一个包含两个有效载荷 种类 的父模型。这样你就不会 运行 进入这种在必要时检查 种类 的讨厌模式。

您的一个表单端点接受一个 Custom1,它有一个关联的 Custom1Validator。您的另一个表单端点接受一个 Custom2,它有一个关联的 Custom2Validator。它们是解耦的。您可以安全地更改一个而不影响另一个。

使用 Fluent 验证条件 (When/Unless)

如果您执意要让 一个 模型负责表示多个请求的负载(请不要),您可以使用 When() 方法由图书馆提供。查看他们关于 conditional rules.

的文档