多个部分验证

时间:2014-04-14 08:33:34

标签: fluentvalidation

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

我想要做的是创建几个部分验证对象的验证器,然后根据当时的要求将其合并到其他验证器中。

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

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

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

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

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

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

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

不起作用。

2 个答案:

答案 0 :(得分: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; }
}

现在Contact实体必须实现两个接口:

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());
    }
}

现在,您可以使用验证器验证任何其他实体中的人员数据或地址数据。您需要做的独特之处是在要验证的实体中实现特定的接口。

<强> [UPDATE]

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

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 :(得分:0)

如果您使用与您相同类型的验证器,它将仅使用该类型上设置的最后一个验证器(在您的情况下为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);