Search code examples
c#fluentvalidation

Use Fluent Validation to compare values in an array


I have an array of objects I am receiving in an HTTP Post body. Looks something like this:

{
  load: {
     stops: [
          { 
            Name: "Stop 1",
            AppointmentDateTime: "02/25/2025T14:00:00"
          },
          { 
            Name: "Stop 2",
            AppointmentDateTime: "02/25/2025T13:00:00"
          },
          { 
            Name: "Stop 3",
            AppointmentDateTime: "02/25/2025T15:30:00"
          }
      ]
   }
}

I have already got a fluid validator in C# working with some of the following code:

RuleForEach(x => x.Stops).ChildRule(stop => 
{
   stop.RuleFor(s => s.Name).NotEmpty().NotNull();
   stop.RuleFor(s => s.AppointmentDateTime).NotEmpty().NotNull().Must(x => x > x.DateTime.Now).WithMessage("AppointmentDateTime must be in the future");
}

While this works, I would like to validate that the items in the array are after each other. Stop 2 should have an appointment time after stop 1 and stop 3 after stop 2 etc. I can't find a way in the code to do something like a for loop where I can save the previous item value. Ideally I was going to do something like:

Stop? prevStop = null;
foreach (stop in stops) {
  if (!prevStop is null)
    {
     DO SOMETHING TO validate that prevStop.AppointmentDateTime > stop.AppointmentDateTime;
    }
  prevStop = stop;
}

While there is a foreach like way of testing the items, I don't see a way to save the previous item for comparison to the current one.


Solution

  • you can create Custom Validator as follows :

    
    public class LoadValidator : AbstractValidator<Load>
    {
        public LoadValidator()
        {
            RuleForEach(x => x.Stops).ChildRule(stop =>
            {
                stop.RuleFor(s => s.Name).NotEmpty().NotNull();
                stop.RuleFor(s => s.AppointmentDateTime).NotEmpty().NotNull().Must(x => x > DateTime.Now).WithMessage("AppointmentDateTime must be in the future");
            });
    
            RuleFor(x => x.Stops).Must(stops => BeInChronologicalOrder(stops)).WithMessage("Stops must be in chronological order");
        }
    
        private bool BeInChronologicalOrder(Stop[] stops)
        {
            if (stops == null || stops.Length <= 1)
            {
                return true;
            }
    
       
            //iterate through the array
            //and compare the AppointmentDateTime of the current stop 
            //with the AppointmentDateTime of the previous stop.
            //If any stop's appointment time
            //is not after the previous stop's appointment time, the   validation fails,
            //and the method returns false.
           
            for (int i = 1; i < stops.Length; i++)
            {
                if (stops[i].AppointmentDateTime <= stops[i - 1].AppointmentDateTime)
                {
                    return false;
                }
            }
    
            return true;
        }
    }