44

I've been trying to work out how to create a FluentValidation rule that checks if the instance of an object it's validating is not null, prior to validating it's properties.

I'd rather encapsulate this null validation in the Validator rather then doing it in the calling code.

See example code below with comments where the required logic is needed:

namespace MyNamespace
{
    using FluentValidation;

    public class Customer
    {
        public string Surname { get; set; }
    }

    public class CustomerValidator: AbstractValidator<Customer> 
    {
        public CustomerValidator() 
        {
            // Rule to check the customer instance is not null.

            // Don't continue validating.

            RuleFor(c => c.Surname).NotEmpty();
        }
    }

    public class MyClass
    {
        public void DoCustomerWork(int id)
        {
            var customer = GetCustomer(id);
            var validator = new CustomerValidator();

            var results = validator.Validate(customer);

            var validationSucceeded = results.IsValid;
        }

        public Customer GetCustomer(int id)
        {
            return null;
        }
    }
}

So my question is how do I check in the CustomerValidator() constructor that the current instance of customer is not null and abort further rule processing if it is null?

Thanks in advance.

4

9 回答 9

35

您应该能够覆盖类中的Validate方法CustomerValidator

public class CustomerValidator: AbstractValidator<Customer> 
{
    // constructor...

    public override ValidationResult Validate(Customer instance)
    {
        return instance == null 
            ? new ValidationResult(new [] { new ValidationFailure("Customer", "Customer cannot be null") }) 
            : base.Validate(instance);
    }
}
于 2013-06-13T19:57:17.447 回答
27

我现在无法真正测试,但您可以尝试覆盖Validate,或在When块中包含规则:

public CustomerValidator()
{
     When(x => x != null, () => {
         RuleFor(x => x.Surname).NotEmpty();
         //etc.
     });
}
于 2013-06-13T19:56:17.280 回答
11

对于那些使用 >6.2.1 版本的用户,您需要改写此签名,以实现与 @chrispr 相同的效果:

public override ValidationResult Validate(ValidationContext<T> context)
{
    return (context.InstanceToValidate == null) 
        ? new ValidationResult(new[] { new ValidationFailure("Property", "Error Message") })
        : base.Validate(context);       
}

/// NETCORE-3.1的示例
/// fluentvalidator-9.5.0

public class Organisation
{ 
    public string Name { get; set; }
}

public class OrganisationValidator : AbstractValidator<Organisation>
{
    public OrganisationValidator()
    {
        RuleFor(x => x.Name).NotNull().MaximumLength(50);
    }

    protected override bool PreValidate(ValidationContext<Organisation> context, ValidationResult result)
    {
        if (context.InstanceToValidate == null) {
            result.Errors.Add(new ValidationFailure("", "org is null"));
            return false;
        }
        return base.PreValidate(context, result);
    }
}


[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void ValidateWithNull()
    {
        var validator = new OrganisationValidator();
        Organisation organisation = null;
        var result = validator.Validate(organisation);
        // result.Errors[0].ErrorMessage == "org is null";
    }
}
于 2016-06-30T09:14:40.807 回答
6

这是一篇较旧的帖子,但希望更新答案以包括 FluentValidation 文档中的以下内容:

使用预验证

如果您需要在每次调用验证器时运行特定代码,您可以通过重写 PreValidate 方法来实现。此方法采用 ValidationContext 和 ValidationResult,您可以使用它们来自定义验证过程。

public class MyValidator : AbstractValidator<Person> {
  public MyValidator() {
    RuleFor(x => x.Name).NotNull();
  }

  protected override bool PreValidate(ValidationContext<Person> context, ValidationResult result) {
    if (context.InstanceToValidate == null) {
      result.Errors.Add(new ValidationFailure("", "Please ensure a model was supplied."));
      return false;
    }
    return true;
  }
}

https://fluentvalidation.net/start#using-prevalidate

于 2018-10-12T17:24:29.827 回答
3

我继承了流利的 AbstractValidator 并创建了一个 NullReferenceAbstractValidator 类:

public class NullReferenceAbstractValidator<T> : AbstractValidator<T>
{
    public override ValidationResult Validate(T instance)
    {
        return instance == null
            ? new ValidationResult(new[] { new ValidationFailure(instance.ToString(), "response cannot be null","Error") })
            : base.Validate(instance);
    }
}

然后从该类继承每个需要空引用检查的验证器:

public class UserValidator : NullReferenceAbstractValidator<User>
于 2014-02-17T21:39:26.953 回答
3

由于上述解决方案对我不起作用(FluentValidation,Net45 的版本 = 6.2.1.0),我发布了我所做的。这只是ValidateAndThrow扩展方法的简单替换/包装器。

public static class ValidatorExtensions
{
    public static void ValidateAndThrowNotNull<T>(this IValidator<T> validator, T instance)
    {
        if (instance == null)
        {
            var validationResult = new ValidationResult(new[] { new ValidationFailure("", "Instance cannot be null") });
            throw new ValidationException(validationResult.Errors);
        }
        validator.ValidateAndThrow(instance);
    }
}
于 2016-05-18T02:02:55.767 回答
2

使用级联模式。

这是文档中的示例。

RuleFor(x => x.Surname).Cascade(CascadeMode.StopOnFirstFailure).NotNull().NotEqual("foo");

同样来自文档:

如果 NotNull 验证器失败,则 NotEqual 验证器将不会被执行。如果您有一个复杂的链,其中每个验证器都依赖于前一个验证器才能成功,这将特别有用。

于 2017-05-27T04:02:01.637 回答
2

覆盖 EnsureInstanceNotNull 如下

protected override void EnsureInstanceNotNull(object instanceToValidate)
{
    if(instanceToValidate==null)
      throw new ValidationException("Customer can not be null");
}
于 2017-10-04T04:31:11.927 回答
2

通过自定义()。当另一个字段的验证基于您当前字段的验证时,它也非常有用。

ruleBuilder.Custom((obj, context) =>
        {
            if (obj != null)
            {
                var propertyName = <field where should be validation>;
                context.AddFailure(propertyName, "'Your field name' Your validation message.");
            }
        });
于 2018-01-09T10:50:19.760 回答