Question

I'm having a play around with http://fluentvalidation.codeplex.com/ to validate some domain models.

I have a typical scenario where I want to validate a string, for example...

RuleFor(x => x.MyString).NotNull().NotEmpty().Length(2, 20).WithMessage("Please provide a string with a minium of 2 characters.");

...that all works just fine and dandy until I create a unit test that specifies that the MyString property must have a length of 2-20 characters not including whitespace.

So myObject.myString = "A" + new String(' ', 10); should fail validation.

I can get all this working with a .Must(IsValidString) and write all the logic myself in a...

    private bool IsValidString(string myString)
    {
       if(String.IsNullOrEmpty(myString))
           return false;

       // Then work on myString.Trim()'ed value. 
    }

...but then I loose all the lovely fluentvalidationness!!

Obviously I can make my unit test pass using this method, and all will be happy in my little the world, but am I missing a trick?

Many thanks.

Was it helpful?

Solution 2

A little peek into the dll FluentValidation dll with http://ilspy.net/ and I was able to get inspiration to make the following TrimmedLengthValidator...

public static class DefaultValidatorExtensions
    {
        public static IRuleBuilderOptions<T, string> TrimmedLength<T>(this IRuleBuilder<T, string> ruleBuilder, int min, int max)
        {
            return ruleBuilder.SetValidator(new TrimmedLengthValidator(min, max));
        }
    }

public class TrimmedLengthValidator : PropertyValidator, ILengthValidator, IPropertyValidator
    {
        public int Min { get; private set; }
        public int Max { get; private set; }

        public TrimmedLengthValidator(int min, int max)
            : this(min, max, () => Messages.length_error)
        { }

        public TrimmedLengthValidator(int min, int max, Expression<Func<string>> errorMessageResourceSelector)
            : base(errorMessageResourceSelector)
        {
            this.Max = max;
            this.Min = min;

            if (max != -1 && max < min)
                throw new ArgumentOutOfRangeException("max", "Max should be larger than min.");
        }

        protected override bool IsValid(PropertyValidatorContext context)
        {
            if (context.PropertyValue == null)
                return true;

            int length = context.PropertyValue.ToString().Trim().Length;

            if (length < this.Min || (length > this.Max && this.Max != -1))
            {
                context.MessageFormatter.AppendArgument("MinLength", this.Min).AppendArgument("MaxLength", this.Max).AppendArgument("TotalLength", length);
                return false;
            }
            return true;
        }
    }

...which means I can simply change my validation to:

RuleFor(x => x.myString).NotEmpty().TrimmedLength(2, 20).WithMessage("Please provide a string with a minium of 2 characters.");

Rock on!

OTHER TIPS

Use Transform to prepare the value prior to validation:

Version: 9.5+

Transform(i => i.InitialString, v => v?.Trim()).NotEmpty();

Versions: 9.0 - 9.4:

RuleFor(x => x.InitialString).Transform(v => v?.Trim()).NotEmpty();

I'm not really familiar with this framework, but would something like this work? You'll still keep most of your fluency, but checking for whitespace requires you to either write your custom validator (which is a big hassle), or by using the Predicate validator

RuleFor(x => x.MyString)
    .NotNull()
    .NotEmpty()
    .Length(2, 20)
        .WithMessage("Please provide a string with a minium of 2 characters.")
    .Must(myString => myString == Regex.Replace( myString, @"s", "" ))
RuleFor(x => x.myStringBefore).Transform(c => c.Trim()).NotEmpty();

Use Transform

You can use trim or substring... (after transforming) fluent validation allows to apply any valid rules to the transformed value.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top