【问题标题】:FluentValidation: Update the validated model for some optional properties inside the validatorFluentValidation:更新验证器中一些可选属性的验证模型
【发布时间】:2019-09-24 15:43:26
【问题描述】:

我想知道是否可以在 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,并将警告保存在变量中,但仍将模型视为有效。

【问题讨论】:

  • 更新传递给验证的对象不是一个好主意,Validator应该只验证输入实体,并返回验证结果就是这样。
  • 更新了我的答案

标签: c# fluentvalidation


【解决方案1】:

实际上操纵传入的值不仅仅是验证。所以 FluentValidator 可能不是最好的地方。将那个特定的逻辑放在 setter 中怎么样?:

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


编辑:回应 cmets 提出的问题

因此,关于您希望显示警告消息的愿望。我假设您已经知道如何在 FluentValidation 中执行此操作。这似乎是一个不那么有趣的话题,至少在我查找 this 资源时是如此。因此,我将在此响应中保留实际的警告逻辑。

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

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 必须是公开的,您才能使用 FluentValidation 访问它(我能够确认这一点)。如果您不喜欢这样,也许您可​​以使用一种方法访问它。此外,它可能只是一个布尔值,当您发现它的值为 true 时,您可以在其他地方设置文本。

最后,为了确认您最初的方法可能无法如您所愿,我在 GitHub 存储库上发现了一个封闭的issue,有人询问如何做类似的事情。以下是 JeremySkinner 的回复:

您好,FluentValidation 仅对预先填充的对象执行验证,它不会修改/更改属性的值。从技术上讲,您可以使用可以完成工作的自定义验证器来执行此操作,但我不会真的推荐它。

【讨论】:

  • 问题是,当我在验证时,我想在响应中添加一些警告,指出某些值由于无效值而被忽略。
  • 另外,如果模型有不同的消费者,每个消费者可能有不同的规则。
  • 我正在考虑一个潜在的解决方案来解决您的第一个问题。 FluentValidator 是否允许针对私有字段/属性的规则?
  • 我不这么认为,因为验证器是一个单独的类,您无法访问私有字段/属性。
  • @BudaGavril 如果您在使用 FluentValidator 时遇到性能问题,这里有一个带有 FastValidator 的 nuget 包,它执行相同的工作但速度提高了 100 倍 nuget.org/packages/FastValidator
【解决方案2】:

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

    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    
            }

        }

【讨论】:

    【解决方案3】:

    我已经解决了:

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

    【讨论】:

    • OnAnyFailure 现在在最新版本中已过时,但我们可以通过使用自定义规则来更新模型 RuleFor(x => x).Custom((customer, context) => { if(customer.Address? .length>150) customer.Address = customer.Address.Substring(0, 150); });
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-21
    • 2023-03-21
    • 1970-01-01
    相关资源
    最近更新 更多