FluentValidation:更新验证器内部一些可选属性的验证模型

FluentValidation: Update the validated model for some optional properties inside the validator

我想知道是否可以在 FluentValidations 验证器中为一些可选参数更新验证模型,以防这些参数无效?

这是一些代码:

public class Customer
{
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
}

public class CustomerValidator : AbstractValidator<Customer>
{
    public CustomerValidator()
    {
        RuleFor(customer => customer.FirstName).NotNull().Length(5, 100);
        RuleFor(customer => customer.LastName).NotNull().Length(5, 100);
        RuleFor(customer => customer.MiddleName).Length(5, 100).When(c => string.IsNullOrWhiteSpace(c.MiddleName));
    }
}

我是这样使用它的:

        Customer customer = new Customer { FirstName = "first name", LastName = "last name" };
        CustomerValidator validator = new CustomerValidator();

        var result = validator.Validate(customer);

        Console.WriteLine(result.IsValid);

所以在我的例子中,我想在中间名无效时将其设置为 null,并在变量中保存警告,但仍将模型视为有效。

实际上操作传入的值不仅仅是验证。所以 FluentValidator 可能不是它的最佳位置。如何将特定逻辑放入 setter?:

string middleName;
public string MiddleName { 
    get => middleName; 
    set { 
        middleName = 
            value.Length < 5 || value.Length > 100 
            ? null 
            : value; 
    } 


编辑:回应评论中提出的问题

因此,关于您希望显示一条警告消息。我假设您已经知道如何在 FluentValidation 中执行此操作。这似乎是一个不太有趣的话题,至少在我查找 资源时是这样。所以我会把实际的警告逻辑从这个响应中去掉。

但是,为了让您有机会发出这样的警告,您需要将其存储在某个地方。因此,只需为此添加一个字段并在您的 setter 逻辑中处理它:

public string middleNameWarning;

string middleName;
public string MiddleName { 

    get => middleName; 

    set { 

        if (value.Length < 5 || value.Length > 100) {
            middleName = null;
            middleNameWarning = 
                $"'{value}' is not a valid MiddleName.  It was reset to null. "
                + "Set MiddleName explicitly to a valid value to remove this warning.";
        }
        else {
            middleName = value;
            middleNameWarning = null;
        }

    }

}

middleNameWarning 必须是 public 才能使用 FluentValidation 访问它(我能够确认这一点)。如果您不喜欢那样,也许您可​​以通过一种方法访问它。此外,它可能只是一个布尔值,当您发现它的值为真时,您可以在其他地方设置文本。

最后,为了确认您最初的方法可能无法像您希望的那样工作,我在 GitHub 存储库中发现了一个已关闭的 issue,供询问如何做类似事情的人使用。这是 JeremySkinner 的回复:

Hi, FluentValidation only performs validation on a pre-populated object, it doesn't modify/change the values of properties. You could technically do this with a custom validator that does the work, but I wouldn't really recommend it.

更新传递给验证的对象不是一个好主意,Validator 应该只验证输入实体,return 验证结果就是这样。我建议你有两个验证器。一个用于业务关键规则,一个用于警告,因此如果第一个验证器 returns 该模型无效,您 return 结果,如果没有严重错误,则您验证警告,然后您可以决定继续或不要和你的模型一起操纵! 例如

    public class Customer
{
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
}

public class CustomerCriticalValidator : AbstractValidator<Customer>
{
    public CustomerValidator()
    {
        RuleFor(customer => customer.FirstName).NotNull().Length(5, 100);
        RuleFor(customer => customer.LastName).NotNull().Length(5, 100);
    }
}

public class CustomerWarningValidator : AbstractValidator<Customer>
{
    public CustomerValidator()
    {

        RuleFor(customer => customer.MiddleName).Length(5, 100).When(c => string.IsNullOrWhiteSpace(c.MiddleName));
    }
}

然后在代码中

            Customer customer = new Customer();

        CustomerCriticalValidator criticalValidator = new CustomerCriticalValidator();

        CustomerWarningValidator warningValidator = new CustomerWarningValidator();

        var validationResult = criticalValidator.Validate(customer);
        if (validationResult.IsValid)
        {
            var result = warningValidator.Validate(customer);
            if (!result.IsValid)
            {
                //DO what you need with customer    
            }

        }

我已经解决了:

public class CustomerValidator : AbstractValidator<Customer>
{
    public CustomerValidator()
    {
        RuleFor(customer => customer.FirstName).NotNull().Length(5, 100);
        RuleFor(customer => customer.LastName).NotNull().Length(5, 100).OnAnyFailure((customer) => 
        {
            customer.LastName = null;
            customer.Warnings.Add(nameof(customer.LastName));
        });
        RuleFor(customer => customer.MiddleName).Length(5, 100).When(c => string.IsNullOrWhiteSpace(c.MiddleName));
    }
}