fluentvalidation InclusiveBetween dynamically set range

Im using FluentValidation

I would like to do a range validation using:

InclusiveBetween

  RuleFor(x => x.Height)
             .InclusiveBetween(x=> x.min, x.max).

I want to be able to get the 'from' and 'to' values dynamically from the model..rather than being hardcoded in the validator

Is this possible?

Thanks

Well, there's nothing in FluentValidation for that.

But you could write your own extension method (and validator), something like that (fast shot, so you'll have to make this better, but you've got the idea).

//the extension method
public static class ValidationExtensions
   {
        public static IRuleBuilder<T, TProperty> InclusiveBetween<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, Expression<Func<T, TProperty>> fromExpression, Expression<Func<T, TProperty>> toExpression)
        {
           var fromFunc = leftExpression.Compile();
           var toFunc = rightExpression.Compile();
           return ruleBuilder.SetValidator(new InclusiveBetweenPropertyValidator(fromFunc.CoerceToNonGeneric(), fromExpression.GetMember(), toFunc.CoerceToNonGeneric(), toExpression.GetMember()));
        }
   }

Then the Validator class

public class InclusiveBetweenPropertyValidator : PropertyValidator, IBetweenValidator, IPropertyValidator
{
    public  Func<object, object> FromFunc { get; set; }
    public MemberInfo FromMemberInfo { get; set; }

    public Func<object, object> ToFunc { get; set; }
    public MemberInfo ToMemberInfo { get; set; }

    public IComparable From { get; private set; }
    public IComparable To { get; private set; }

    public InclusiveBetweenPropertyValidator(Func<object, object> fromFunc, MemberInfo fromMember, Func<object, object> toFunc, MemberInfo toMember)
        : base((() => Messages.inclusivebetween_error))
    {
        FromFunc = fromFunc;
        FromMemberInfo = fromMember;
        ToFunc = toFunc;
        ToMemberInfo = toMember;
    }


    protected override bool IsValid(PropertyValidatorContext context)
    {
        var comparable = (IComparable)context.PropertyValue;
        From = (IComparable)this.FromFunc(context.Instance);
        To = (IComparable)this.ToFunc(context.Instance);

        if (comparable == null || FluentValidation.Internal.Comparer.GetComparisonResult(comparable, From) >= 0 && FluentValidation.Internal.Comparer.GetComparisonResult(comparable, To) <= 0)
            return true;
        context.MessageFormatter.AppendArgument("From", string.Format("{0} ({1})", FromMemberInfo.Name, From)).AppendArgument("To", string.Format("{0} ({1})",ToMemberInfo.Name, To)).AppendArgument("Value", context.PropertyValue);
        return false;
    }
}

usage :

RuleFor(x => x.Height)
             .InclusiveBetween(x=> x.min, x.max)

Alternatively, you can use the old email validation behaviour that uses a regular expression consistent with the .NET 4.x version of the ASP.NET EmailAddressAttribute.You can use this behaviour in FluentValidation by calling RuleFor(x => x.Email).EmailAddress(EmailValidationMode.Net4xRegex).

This is similar to Raphaël's answer, but is more of a case-by-case usage as opposed to a general usage extension.

RuleFor(x => x).Must(HeightValidation);

private static bool HeightValidation(Model m)
{
    return m.Height >= m.min && m.Height <= m.max;
}

This is how I access the valid ranges of the properties at runtime, contributing to dynamic validation that cannot be done with DataAnnotations. MainWindowViewModel class Finally, the MainWindowViewModel class instantiates the Problem5 model object with the appropriate validator.

If you don't want to write an extension you could use the additional overload of the Predicate Validator - which also accepts an instance of the parent object - like this:

RuleFor(x => x.Height)
    .Must((model, height) => height >= model.Min && height <= model.Max);

Most common attributes are: Required (must have value), StringLength (Set maximum length), Range (value must be in between the range), DataType… For more strict rules, you can apply RegularExpression to limit what users can input (ex: must use letters, no special characters).

Let's imagine your Model as follow:

public class YourModel
    {
        public int Height { get; set; }
        public int Min { get; set; }
        public int Max { get; set; }
    }

then Validation will be as follow:

public class YourModelValidation : AbstractValidator<YourModel>
    {
        public YourModelValidation(int min,int max)
        {
            RuleFor(x => x.Height).InclusiveBetween(min, max);
        }
    }

then Validation usage is :

var validation = new YourModelValidation(model.Min,model.Max).Validate(model);

as you can see dynamic parameters are passed in validation's Constructor.

1 fluentvalidation InclusiveBetween dynamically set range Oct 22 '19 1 Cannot get swagger xml comments to work (web api 2) Apr 30 '18 1 XML signature: Verify that all required elements have been signed Jun 18 '18

Note that FluentValidation will also work with ASP.NET MVC’s client-side validation, but not all rules are supported. For example, any rules defined using a condition (with When/Unless), custom validators, or calls to Must will not run on the client side.

Getting Started¶. FluentValidation supports integration with ASP.NET Core 2.1 or 3.1 (3.1 recommended). Once enabled, MVC will use FluentValidation to validate objects that are passed in to controller actions by the model binding infrastructure.

Learn how to install FluentValidation and get started creating validators. Built-in Validators. A complete list of built-in validators. Custom Validators.

Comments
  • will this let you have automatic client side validation?
  • @raklos No idea. Big doubt, as in FluentValidation doc, it's stated that custom validators are not supported for client-side validation. But you may test...
  • thank you so much.