【问题标题】:Multiple partial validations多个部分验证
【发布时间】:2022-03-04 06:23:31
【问题描述】:

我正在开发的系统使用 FluentValidation (v5.0.0.1)。

我想做的是创建几个验证器来部分验证一个对象,然后我可以根据当时的需要将它们组合到其他验证器中。

例如,假设我的班级有名字和地址。 (这不能像示例中那样拆分为单独的类)。

对于场景 1,我只想验证名称,所以我编写了一个名称验证器类。

对于场景2,我只想验证地址,所以我写了一个地址验证器类。

对于场景 3,我想同时验证名称和地址,因此我想编写一个新的验证器类,它调用名称验证器,然后调用地址验证器。

我不想在不同的地方重复代码,这就是我希望它们分开的原因。我也不想使用 When 运算符,因为无法从对象的内容中确定何时。

我知道我可以调用 SetValidator,但是如何编写调用呢?

RuleFor(j=>j).SetValidator(new NameValidator());
RuleFor(j=>j).SetValidator(new AddressValidator());

没用。

【问题讨论】:

    标签: fluentvalidation


    【解决方案1】:

    我将用这个例子来解释解决方案。我将验证此联系人实体:

    public class Contact
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    
        public string Address1 { get; set; }
        public string Address2 { get; set; }
        public string City { get; set; }
        public string PostalCode { get; set; }
    }
    

    要求是验证 FirstName 和 LastName,然后是 Address1、Address2、City、PostalCode,并且可以在其他实体中重用我们的验证器。

    创建接口来定义特定实体是什么。

    public interface IAmPerson
    {
        string FirstName { get; set; }
        string LastName { get; set; }
    }
    
    public interface IHaveAddress
    {
        string Address1 { get; set; }
        string Address2 { get; set; }
        string City { get; set; }
        string PostalCode { get; set; }
    }
    

    现在联系人实体必须实现这两个接口:

    public class Contact : IAmPerson, IHaveAddress
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    
        public string Address1 { get; set; }
        public string Address2 { get; set; }
        public string City { get; set; }
        public string PostalCode { get; set; }
    }
    

    然后,为 IAmPerson 实体创建第一个验证器

    public class PersonValidator : AbstractValidator<IAmPerson>
    {
        public PersonValidator()
        {
            RuleFor(data => data.FirstName).Length(3, 50).WithMessage("Invalid firstName");
            RuleFor(data => data.LastName).Length(3, 50).WithMessage("Invalid LastName");
        }
    }
    

    IHaveAddress 实体的第二个

    public class AddressValidator : AbstractValidator<IHaveAddress>
    {
        public AddressValidator()
        {
            RuleFor(data => data.Address1).NotNull().NotEmpty().WithMessage("Invalid address1");
            RuleFor(data => data.Address2).NotNull().NotEmpty().WithMessage("Invalid address2");
            RuleFor(data => data.City).NotNull().NotEmpty().WithMessage("Invalid City");
            RuleFor(data => data.PostalCode).NotNull().NotEmpty().WithMessage("Invalid PostalCode");
        }
    }
    

    自定义验证器的使用方式

    public class ContactValidator: AbstractValidator<Contact>
    {
        public ContactValidator()
        {
            RuleFor(contact => contact).SetValidator(new PersonValidator());
            RuleFor(contact => contact).SetValidator(new AddressValidator());
        }
    }
    

    现在您可以使用您的验证器来验证任何其他实体中的个人数据或地址数据。您需要做的唯一事情是在您要验证的实体中实现特定的接口。

    [更新]

    您可以通过添加扩展方法来提高代码的可读性

    public static class ValidatorExtensions
    {
        public static IRuleBuilderOptions<T, IHaveAddress> MustHaveAValidAddress<T>(this IRuleBuilder<T, IHaveAddress> ruleBuilder) 
        {
            return ruleBuilder.SetValidator(new AddressValidator());
        }
    
        public static IRuleBuilderOptions<T, IAmPerson> MustBeAValidPerson<T>(this IRuleBuilder<T, IAmPerson> ruleBuilder)
        {
            return ruleBuilder.SetValidator(new PersonValidator());
        }
    }
    

    这是使用我刚刚添加的扩展方法的最终结果:

    RuleFor(contact => contact).MustBeAValidPerson();
    RuleFor(contact => contact).MustHaveAValidAddress();
    

    【讨论】:

    • 很久以前我问过这个问题,但我认为这正是问题所说的我不想做的事情。如果地址是多行(地址1、地址2、城镇、国家、邮政编码)怎么办?我不想为地址 1、地址 2 等编写验证器,我只想要一个用于构成地址的所有字段。从原始问题的底部: RuleFor(data=>data).SetValidator (etc) doesn't work.
    • 如果我理解得很好,请告诉我。您是否尝试验证这样的 dto?:'class Contact { public string Name { get;放; } 公共字符串姓氏 { 获取;放; } 公共字符串地址 1 { 获取;放; } 公共字符串地址 2 { 获取;放; } 公共字符串城镇 { 获取;放; } 公共字符串 PostCode { 获取;放; } }'
    • 是的。我想制定两条规则。规则 1 验证姓名和姓氏,规则 2 验证地址 1、地址 2、城镇和邮政编码。有时我想验证 Rule1 和/或 Rule2 的组合。
    • @Neil 检查我更新的答案。让我知道它是否有助于解决您的问题。
    • 一个很有趣的方法。
    【解决方案2】:

    如果您在与您所拥有的类型相同的类型上设置验证器,它将仅使用在该类型上设置的最后一个验证器(在您的情况下为AddressValidator)。您可以创建一些方法来封装验证并使用Must。

    请注意,您将无法在这些不同的验证中重复使用相同的错误代码或错误消息。

    public static class Validations
    {
        public static bool ValidateName(string name)
        {
            return name != null; //Or any other validation
        }
    
        public static bool ValidateAddress(string address)
        {
            return !string.IsNullOrEmpty(address); //Or any other validation
        }
    }
    

    场景 1

     RuleFor(j=>j.Name).Must(Validations.ValidateName);
    

    场景 2

     RuleFor(j=>j.Address).Must(Validations.ValidateAddress);
    

    场景 3

     RuleFor(j=>j.Name).Must(Validations.ValidateName);
     RuleFor(j=>j.Address).Must(Validations.ValidateAddress);
    

    【讨论】:

    • 感谢@bpruitt-goddard。想要制定单独规则的主要原因是 .When 和 .WithMessage 部分。目前,我必须重复这些位(有时很复杂),这并不是真正的 DRY。我目前正在尝试stackoverflow.com/questions/13198471/…,这还可以,但不是我想要的
    猜你喜欢
    • 2012-09-17
    • 1970-01-01
    • 2010-09-30
    • 1970-01-01
    • 1970-01-01
    • 2013-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多