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.
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();
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!
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