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)
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);
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With