51

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.

Bern
  • 7,808
  • 5
  • 37
  • 47

11 Answers11

37

EDIT 2022-07-19
As some commenters have pointed out, please check out answer https://stackoverflow.com/a/52784357/1943 for a newer implementation. I haven't personally vetted, but it's worth a try to give that a go first.

If you're using an older version, or you enjoy nostalgia, my original answer below is from 2013.


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);
    }
}
Matthew
  • 24,703
  • 9
  • 76
  • 110
  • You could even cut it down to this: return instance == null ? new ValidationResult(new [] { new ValidationFailure("Customer", "Customer cannot be null") }) : base.Validate(instance); – Bern Jun 13 '13 at 20:03
  • 8
    I'm using the latest version of FluentValidation and the Validate() doesn't even fire for me. This doesn't appear to be possible anymore. – Sergey Akopov Mar 19 '15 at 21:58
  • @SergeyAkopov I don't understand how `Validate` doesn't fire since you would be the one calling the validate method. – Matthew Mar 20 '15 at 13:17
  • This didn't work for me on 6.2.0 as I am calling `ValidateAndThrow` extension method. It blows up before even getting to my overridden `Validate` method.. – Lee Campbell May 09 '16 at 07:04
  • @LeeCampbell : Did you find any solution to handle null model in web api ? – Amit Kumar May 17 '16 at 18:11
  • I created another extension method. See answer below http://stackoverflow.com/questions/17095791/fluentvalidation-rule-for-null-object/#answer-37289001 – Lee Campbell May 18 '16 at 02:06
  • @Matthew, depends entirely on how you use the library. if you explicitly call validate than this will work. If you use WebAPI integration, than having null object will prevent Fluent from discovering the type-specific validator and hence `Validate()` will not be called. – Sergey Akopov Feb 27 '18 at 19:20
  • 4
    As of 8.1.3 ([link](https://github.com/JeremySkinner/FluentValidation/commit/820e9c3cbbec621c4e2b533cfca79edc6d73148b)), it looks like you can override the `PreValidate()` method if you want to implement custom logic to handle null models that are passed to the validator. – Mass Dot Net Feb 06 '19 at 21:05
  • 3
    This solution no longer works for new versions of FluentValidation – Akin_Glen Nov 06 '20 at 08:02
  • 1
    See https://stackoverflow.com/a/52784357/1943 (override PreValidate()) for updated approach from the developer. – Ryan Shripat Jul 18 '22 at 22:05
  • 1
    Thanks for the comments, I updated my answer to hopefully save people time and forward to the newer implementation. – Matthew Jul 19 '22 at 13:35
30

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.
     });
}
johnny 5
  • 19,893
  • 50
  • 121
  • 195
Patryk Ćwiek
  • 14,078
  • 3
  • 55
  • 76
  • Nice, but is there a way I can apply a validation message to it, i.e. .WithMessage("Customer does not exist."); ? – Bern Jun 13 '13 at 19:58
  • 3
    @Bern Maybe you could do `RuleFor(x => x).NotNull().WithMessage("Customer does not exist");`, but outside the `When` block? – Patryk Ćwiek Jun 13 '13 at 20:00
  • Thanks mate, yeah I tried that and it threw an exception. I think @Matthew managed to nail it above. – Bern Jun 13 '13 at 20:01
  • 1
    Alright, I guess then that overriding `Validate` is the best way to go here – Patryk Ćwiek Jun 13 '13 at 20:01
14

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";
    }
}
Elmar
  • 332
  • 1
  • 3
  • 11
  • 6
    This method neither not fired for WebApi. – Alexander Pavlenko Aug 07 '16 at 01:38
  • @Akin_Glen Could you elaborate? By coincidence I have returned to this answer I edited over 2.5 years ago on the same day you have added your comment. I have this working in a .NetStandard 2.1 class library. The validator used to throw on null input, but returns a ValidationFailure using the above solution – user1007074 Nov 06 '20 at 16:13
  • I'm using version 9.2.0 and basically after registering the validator in my startup, it should automatically pick up instances where an object is null. That doesn't happen with this example, for some reason. I had to use @user7617828's answer for this – Akin_Glen Nov 09 '20 at 12:42
  • I've tries all examples from this post - none works. – Alex Blokha Jan 28 '21 at 23:46
  • @AlexBlokha, could you perhaps provide a bit more context of your specific situation? Which framework are you using net45x, netcore? – Elmar Feb 01 '21 at 08:29
  • .net core 3.1, WebApi. It seems those tricks are not working under WebApi. – Alex Blokha Feb 01 '21 at 09:46
  • @AlexBlokha, i've updated the example that works for netcore 3.1. – Elmar Feb 02 '21 at 13:53
14

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://docs.fluentvalidation.net/en/latest/advanced.html?#prevalidate

Ryan Shripat
  • 5,574
  • 6
  • 49
  • 77
camainc
  • 3,750
  • 7
  • 35
  • 46
3

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);
    }
}
Lee Campbell
  • 10,631
  • 1
  • 34
  • 29
3

Override EnsureInstanceNotNull as below

protected override void EnsureInstanceNotNull(object instanceToValidate)
{
    if(instanceToValidate==null)
      throw new ValidationException("Customer can not be null");
}
CreativeManix
  • 2,162
  • 1
  • 17
  • 29
3

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.");
            }
        });
user7617828
  • 31
  • 1
  • 2
  • This is the only answer that works. I would implore everyone to implement this. If you have an object in your model and you need fluent validator to send an error message if that object is omitted when your API is called, this is how it should be done. Thanks! – Akin_Glen Nov 06 '20 at 08:27
2

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>
chrispr
  • 336
  • 2
  • 6
2

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.

Trevor
  • 4,620
  • 2
  • 28
  • 37
0

You can override a virtual method called EnsureInstanceNotNull as the author recommends here

public class CustomerValidator: AbstractValidator<Customer> 
{
    public CustomerValidator() 
    {
        // Rule to check the customer instance is not null.
        RuleFor(c => c).NotNull();
        // Don't continue validating.

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

    protected override void EnsureInstanceNotNull(object instance) { }
}
0

The commonly accepted PreValidate answer will not work in this instance, as per here:

If you use SetValidator with an AbstractValidator derivative, then it will not be run if the property value is null. This is intentional behaviour as AbstractValidator derivatives are designed to validate the properties of a complex type, which cannot be done if the instance is null. AbstractValidator is not designed for use with simple/primative types or object. Now, if you want to check for null here, then you can precede the SetValidator call with a NotNull rule (although it doesn't seem like this is what you want in this case).

For me the only thing that worked is (my example):

RuleForEach(command => command.ProductDto.ProductInfos).NotNull().WithMessage("Custom Message").SetValidator(new ProductInfoValidator());

Without the "NotNull()", null values will be skipped.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
vpDev
  • 33
  • 5