1

Considering this simplified code:

public class Request
{
    public List<Selection> Selections { get; set; }
}

public class Selection
{
    public decimal Price { get; set; }
}

public class RequestValidator(): AbstractValidator<Request>
{
    public RequestValidator()
    {
        RuleForEach(x => x.Selections).SetValidator(new SelectionValidator());
    }
}

public class SelectionValidator : AbstractValidator<Selection>
{
    public SelectionValidator()
    {
        RuleFor(x => x.Price).NotEqual(0); 
    }
}

If I have a Selection item with Price equal to 0 I do get error message: 'Price' must not be equal to '0'.

What I am missing is reference to which element in collection has this error.

When debugging i can clearly see that Error.PropertyName is set to Selections[0].Price, but formatted name is missing that reference to the item.

Is there a way to correctly format the full property name? Maybe by using .WithMessage() but it does not seems to work.

Example of debug info

To make it clear, my goal is to have error on Nested Items look like this: <CollectionPropertyName>[<index>].<PropertyName> <error text>. ex. Selectons[0].Price must not be equal to 0

Michael D.
  • 1,249
  • 2
  • 25
  • 44
  • I'm not sure that's possible. Take a look at this thread, might be useful? https://stackoverflow.com/questions/27213058/how-can-i-access-the-collection-item-being-validated-when-using-ruleforeach – Nikki9696 Jan 09 '19 at 21:46

2 Answers2

2

You can override the Validate + ValidateAsync method to modify the result:

public abstract class ValidatorWithFullIndexerPath<T> : AbstractValidator<T>
{
    public override ValidationResult Validate(ValidationContext<T> context)
    {
        var result = base.Validate(context);
        FixIndexedPropertyErrorMessage(result);

        return result;
    }
    public override async Task<ValidationResult> ValidateAsync(ValidationContext<T> context, CancellationToken cancellation = default(CancellationToken))
    {
        var result = await base.ValidateAsync(context, cancellation);
        FixIndexedPropertyErrorMessage(result);

        return result;
    }

    protected void FixIndexedPropertyErrorMessage(ValidationResult result)
    {
        if (result.Errors?.Any() ?? false)
        {
            foreach (var error in result.Errors)
            {
                // check if 
                if (Regex.IsMatch(error.PropertyName, @"\[\d+\]") &&
                    error.FormattedMessagePlaceholderValues.TryGetValue("PropertyName", out var propertyName))
                {
                    // replace PropertyName with its full path
                    error.ErrorMessage = error.ErrorMessage
                        .Replace($"'{propertyName}'", $"'{error.PropertyName}'");
                }
            }
        }
    }
}

And, update RequestValidator and any other classes where full path is desired:

public class RequestValidator : ValidatorWithFullIndexerPath<Request>
Xiaoy312
  • 14,292
  • 1
  • 32
  • 44
  • Thanks, I already started working in the same direction - was hoping there is something build in available. – Michael D. Jan 10 '19 at 14:52
0

FluentValidation 8.1 contains a new method OverrideIndexer() to support this:

mountain traveller
  • 7,591
  • 33
  • 38