Search code examples
c#fluentvalidation

How to validate a property (via IncludeProperties) within a specific ruleset (via IncludeRuleSets) with FluentValidation?


Spent a day on searching an answer.

I have the following validator (please note that there are different rules for AgeMin property depends on RuleSet.):

class WeirdValidator : AbstractValidator<TestClass>
{
    
    public WeirdValidator()
    {
        public const string CustomRuleSet = nameof(CustomRuleSet);

        RuleFor(instance => instance.AgeMin).NotEmpty().WithMessage("Empty error. {PropertyName}").ScalePrecision(2, 3).WithMessage("Scale error. {PropertyName}");
        RuleFor(instance => instance.FirstInt).NotEmpty().WithMessage("Empty error. {PropertyName}");

        RuleSet(CustomRuleSet, () => {
            RuleFor(instance => instance.AgeMin).Empty().WithMessage("{PropertyName} must be empty.");
            RuleFor(instance => instance.SecondInt).NotEmpty().WithMessage("{PropertyName} must not be empty.");
        });
    }
}

and the following test class:

class TestClass
{
    public decimal AgeMin { get; set; }
    public int FirstInt { get; set; }
    public int SecondInt { get; set; }
}

FluentValidation allows us to combine validators:

                var result = validator.Validate(instanceToValidate, opt => {
                opt.IncludeRuleSets(WeirdValidator.CustomRuleSet);
                opt.IncludeProperties(nameof(instanceToValidate.AgeMin));});

But it does not work as I expected. it is not intersection of rules, but consequent actions: at first applying all rules from CustomRuleSet and then rule for defined property. I need validation of only selected property of only selected ruleset. I am sure that there is a very simple and elegant solution but it does not illuminate my way yet.

Question: How to validate a specific property within a specific ruleset?


Solution

  • After several days of pain and tears I found a very bad looking solution but, nevertheless, working.

    var validator = new WeirdValidator();
    
    var ruleDescriptor = validator.CreateDescriptor();
    var rulesOfMember = ruleDescriptor.GetRulesForMember(nameof(instanceToValidate.AgeMin));                
    var exactRule = rulesOfMember.FirstOrDefault(c => c.RuleSets.Contains(WeirdValidator.CustomRuleSet));
    
    var errorList = exactRule.Validate(
       new ValidationContext<TestClass>(instanceToValidate, new PropertyChain(),
       new RulesetValidatorSelector(WeirdValidator.CustomRuleSet)));
    

    The main idea was to find the exact rule for exact property and to execute only one. It turned out that IValidationRule has own Validate/ValidateAsync methods.

    If someone knows better/more elegant solution, you are welcome.