Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 313
raklos Avatar asked May 29 '14 16:05

raklos


3 Answers

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)
like image 92
Raphaël Althaus Avatar answered Oct 25 '22 04:10

Raphaël Althaus


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);
like image 3
Johan Maes Avatar answered Oct 25 '22 06:10

Johan Maes


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;
}
like image 1
Ryan Avatar answered Oct 25 '22 05:10

Ryan