1

I make a fluent validation class for this model

 public class OnlineCashierRequest
 {
    Guid InvoiceId { get; set; }      
    string ClientPhoneOrEmail { get; set; }    
    List<IOnlineCashierProduct> Products { get; set; }
 }

public interface IOnlineCashierProduct
{
     string Name { get; set; }
     decimal Amount { get; set; }
     int Count { get; set; }
}

I have a rule for products where Amount equals null. But how can I write in message row index like $"Amount is less zero for row {rowIndex}"

RuleForEach(t => t.Products)
    .Must(x => x.Amount < 0)
    .WithMessage(x => $"[{rowIndex}] Amount must be greater then zero\r\n");
5
  • 1
    Check this stackoverflow.com/questions/27213058/… Commented Jun 11, 2019 at 8:01
  • Why do you need the row index? Commented Jun 11, 2019 at 8:15
  • @chris For log validation exception Commented Jun 11, 2019 at 8:36
  • Would it not be better to use the Name of the IOnlineCashierProduct in the log message? Commented Jun 11, 2019 at 8:48
  • @chris No, there is also may be an error with empty productName Commented Jun 11, 2019 at 9:02

2 Answers 2

1

https://docs.fluentvalidation.net/en/latest/collections.html

RuleForEach(t => t.Products)
    .Must(x => x.Amount < 0)
    .WithMessage(x => $"[{CollectionIndex}] Amount must be greater than zero\r\n");
Sign up to request clarification or add additional context in comments.

1 Comment

Wouldn't it need to be $"[{{CollectionIndex}}] Amount must be greater than zero\r\n" because of the string interpolation?
0

Simon H's answer does the job if you only want the index in the error message.

I'm not recommending anyone do this, but you can always let FluentValidation validate everything else for you and then add custom errors yourself in an override of the Validate method. You can use anything you want from the validation context and have complete control over any error that is generated

using FluentValidation;
using FluentValidation.Results;

public class NeedsValidating
{
    public List<string> StringCollection { get; set; } = new List<string>();
    public int OtherNumber { get; set; }
    public List<string> OtherStringCollection { get; set; } = new List<string>();
}

public class IndexValidator : AbstractValidator<NeedsValidating>
{
    /// <remarks>
    /// The usual Fluent Validation good stuff goes in the constructor
    /// </remarks>
    public IndexValidator()
    {
        RuleFor(nv => nv.OtherStringCollection).NotEmpty().WithMessage("Whatever");
    }

    /// <remarks>
    /// Depending on your use case, you may have to remove errors as well
    /// </remarks>
    public override ValidationResult Validate(ValidationContext<NeedsValidating> context)
    {
        var strings = context.InstanceToValidate.StringCollection;
        var result = base.Validate(context);
        for (int index = 0; index < strings.Count(); index++)
        {
            if (!IsValid(strings, index))
            {
                result.Errors.Add(new ValidationFailure("Custom Property with index", "Custom Message with index", strings[index]));
            }
        }
        // keep going, validate other stuff here if you want to

        return result;
    }

    private bool IsValid(List<string> strings, int index) => false; // throw new NotImplementedException("Whatever you want");

}

Obviously this means that with full control, you're having to write boilerplate code that the validation library normally does for you and is designed to avoid. It doesn't really make sense to write validation like this and use FluentValidation, unless you just want to really customise a couple of things

Example usage:

using Xunit;
using FluentAssertions;

    [Fact]
    public void TestTheValidator()
    {
        var input = new NeedsValidating
        {
             StringCollection = new List<string>{ "Whatever 1", "Whatever 2", "Whatever 3" }
        };

        var result = new IndexValidator().Validate(input);

        result.IsValid.Should().BeFalse();
        result.Errors.Should().HaveCount(4);
        result.Errors.Where(e => e.ErrorMessage == "Whatever").Should().HaveCount(1);
        result.Errors.Where(e => e.ErrorMessage == "Custom Message with index").Should().HaveCount(3);
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.