Dirk S.
Dirk S.

Reputation: 65

FluentValidation chained validators in rule receive cached instance and/or property value?

I need some clarification on how exactly the instance whos property is being validated, is passed down to each validator in a rule.

Let's assume the following:

RuleFor(x => x.MyProperty).SetValidator(new MyCustomValidator()).NotNull();

The MyCustomValidator will set a value on MyProperty if the value is NULL in some cases.
I would expect that the NotNull validation would pass.

However, it appears this is not the case.

When I split up the RuleFor into two seperate calls:

RuleFor(x => x.MyProperty).SetValidator(new MyCustomValidator());
RuleFor(x => x.MyProperty).NotNull();

it works as expected.
But of course, i'd like to avoid having multiple "RuleFor" statements for the same property

This makes me believe that at the start of the "RuleFor", the instance and/or property value being validated is cached/copied and then provided to each validator specified for that rule.

I've also created a fiddle demonstrating this behaviour here: https://dotnetfiddle.net/rDTrDU

Upvotes: 0

Views: 431

Answers (1)

Guru Stron
Guru Stron

Reputation: 143078

The chained one will create one PropertyRule with multiple components and when they are processed FluentValidation caches property value and uses it for every component:

var accessor = new Lazy<TProperty>(() => PropertyFunc(context.InstanceToValidate), LazyThreadSafetyMode.None);

The not chained one creates multiple PropertyRules with one component so after the first rule is validated the MyObject instance will be modified and property accessor will use the new value.

In your case I would try to use PreValidate in MyObjectValidator:

public class MyObjectValidator : AbstractValidator<MyObject>
{
    protected override bool PreValidate(ValidationContext<MyObject> context, ValidationResult result)
    {
        var version = context.RootContextData["SourceVersion"] as Version;
        if (version < new Version(2, 0, 0) && string.IsNullOrWhiteSpace(context.InstanceToValidate.MyProperty))
        {
            Console.WriteLine("\t[DEBUG]: Setting value to 'Hello world'");
            context.InstanceToValidate.MyProperty = "Hello world";
        }
        return true;
    }
  ....
 }

Upvotes: 1

Related Questions