Question

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.

Was it helpful?

Solution

You should be able to override the Validate method in your CustomerValidator class.

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

OTHER TIPS

I can't really test that right now, but you can either try to override Validate, or include the rules in the When block:

public CustomerValidator()
{
     When(x => x != null, () => {
         RuleFor(x => x.Surname).NotEmpty();
         //etc.
     });
}

For those using version >6.2.1 you need to override this signature instead, in order to achieve the same as @chrispr:

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

/// EXAMPLE FOR 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";
    }
}

This is an older post, but want to update the answers to include the following from the FluentValidation documentation:

Using PreValidate

If you need to run specific code every time a validator is invoked, you can do this by overriding the PreValidate method. This method takes a ValidationContext as well as a ValidationResult, which you can use to customise the validation process.

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

I inherited from the fluent AbstractValidator and created a NullReferenceAbstractValidator class instead:

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

and then inherited from that class with each validator that needed a null reference check:

public class UserValidator : NullReferenceAbstractValidator<User>

As the above solutions didn't work for me (FluentValidation, Version=6.2.1.0 for Net45), I am posting what I did. This is just a simple replacement/wrapper for ValidateAndThrow extension method.

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

By means of Custom(). It can be also very helpful when validation of another field is based on validation of your current field.

ruleBuilder.Custom((obj, context) =>
        {
            if (obj != null)
            {
                var propertyName = <field where should be validation>;
                context.AddFailure(propertyName, "'Your field name' Your validation message.");
            }
        });

Use the Cascade mode.

Here is the example from the documentation.

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

Also from the documentation:

If the NotNull validator fails then the NotEqual validator will not be executed. This is particularly useful if you have a complex chain where each validator depends on the previous validator to succeed.

Override EnsureInstanceNotNull as below

protected override void EnsureInstanceNotNull(object instanceToValidate)
{
    if(instanceToValidate==null)
      throw new ValidationException("Customer can not be null");
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top