0

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

1
  • I would say that modifiyng data in a validator in general is not a very good idea. Commented Aug 3, 2021 at 18:43

1 Answer 1

1

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;
    }
  ....
 }
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, I figured out the same thing after I copied the source for the NotNullValidator into the fiddle and hooking it up. context.InstanceToValidate does have its property value updated, but the second parameter of the IsValid method still has the old value. Bummer but oh well, at least now I know for sure :)
@DirkS. see the update. It seems you can override PreValidate in MyObjectValidator to achieve the desired behaviour in the chained case.
Ah cheers! I'll play around with it, see if I can make it fit that way. nice catch!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.