6

I've started using FluentValidation on a WPF project, till now I used it in a simple way checking if a field has been filled or less then n character.

Now I've to check if the value inserted (which is a string...damn old code) is greater then 0. Is there a simple way I can convert it using

RuleFor(x=>x.MyStringField).Somehow().GreaterThen(0) ?

Thanks in advance

4 Answers 4

12

Another solution:

RuleFor(a => a.MyStringField).Must(x => int.TryParse(x, out var val) && val > 0)
.WithMessage("Invalid Number.");
2
  • 1
    Must has really helped me, I was trying with old Transform extension method from older version, but this is super clean and efficient. Commented Sep 20, 2022 at 16:57
  • In my case when it is optional, you can add When: RuleFor(a => a.MyStringField).Must(x => int.TryParse(x, out var val) && val > 0) .WithMessage("Invalid Number.").When(a => !string.IsNullOrEmpty(a.MyStringField)); Commented Nov 6 at 21:32
11

Just write a custom validator like this

public class Validator : AbstractValidator<Test>
    {
        public Validator()
        {
            RuleFor(x => x.MyString)
                .Custom((x, context) =>
                {
                    if ((!(int.TryParse(x, out int value)) || value < 0))
                    {
                        context.AddFailure($"{x} is not a valid number or less than 0");
                    }
                });
        }
    }

And from your place where you need to validate do this

var validator = new Validator();
var result = validator.Validate(test);
Console.WriteLine(result.IsValid ? $"Entered value is a number and is > 0" : "Fail");

Update 11/8/21

If you are going to use this on a large project or API, you are probably better by doing this from the Startup and we don't need to manually call the validator.Validate() in each and every method.

services.AddMvc(options => options.EnableEndpointRouting = false)
                .AddFluentValidation(fv =>
                {
    fv.RegisterValidatorsFromAssemblyContaining<BaseValidator>();
                    fv.ImplicitlyValidateChildProperties = true;
                    fv.ValidatorOptions.CascadeMode = CascadeMode.Stop;
                })
1

You can use transformation: https://docs.fluentvalidation.net/en/latest/transform.html

Transform(from: x => x.SomeStringProperty, to: value => int.TryParse(value, out int val) ? (int?) val : null)
.GreaterThan(0);
0

If you plan to convert the string to a number anyway to make use of it after validation, you can put the conversion to a number in your model class rather than in the validator class. I made use of NaN, but you could accomplish the same with null if you prefer.

public static class StringExtensions
{
    // Convert the string to a double, returning NaN if the conversion fails.
    public static double ToDouble(this string value)
    {
        if (double.TryParse(value, out double result))
            return result;
        return double.NaN;
    }
}

public static class CustomValidationRules
{
    public static IRuleBuilderOptions<T, double> NotNaN<T>(
        this IRuleBuilder<T, double> ruleBuilder)
    {
        return ruleBuilder
            .Must(x => !double.IsNaN(x))
            .WithMessage("'{PropertyName}' must be a number.");
    }
}

public class Foo
{
    private string _weightString;
    public string WeightString
    {
        get => _weightString;
        set
        {
            _weightString = value;
            Weight = value.ToDouble();
        } 
    }
    
    public double Weight { get; private set; }
}

public class FooValidator : AbstractValidator<Foo>
{
    public FooValidator()
    {
        RuleLevelCascadeMode = CascadeMode.Stop;

        RuleFor(x => x.WeightString)
            .NotEmpty()
            .DependentRules(() =>
            {
                RuleFor(x => x.Weight)
                    .NotNaN()
                    .GreaterThan(0);
            })
            .WithName("Weight");
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.