using FluentValidation.Validators;
using FluentValidation.TestHelper;
public static void Main()
UpdateRequest model = new UpdateRequest { Prop1 = null, Prop2 = null, Prop3 = null };
var context = new ValidationContext<UpdateRequest>(model);
context.RootContextData["MyCustomData"] = "Test";
var _validator = new UpdateRequestValidator();
var result = _validator.TestValidate(context);
Console.WriteLine(JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true }));
public record UpdateRequest
public int? Prop1 { get; init; }
public int? Prop2 { get; init; }
public int? Prop3 { get; init; }
public class UpdateRequestValidator : AbstractValidator<UpdateRequest>
public UpdateRequestValidator()
.Cascade(CascadeMode.Stop)
.Custom((request, context) =>
Console.WriteLine("MyCustomData", context.RootContextData["MyCustomData"]);
var properties = request.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(int?))
bool allNull = properties.All(p => ((int?)(p.GetValue(request))).GetValueOrDefault() == 0);
bool allNotNull = properties.All(p => ((int?)(p.GetValue(request))).GetValueOrDefault() != 0);
if (!(allNull || allNotNull))
var firstPropertyWithValue = properties.First(x => x.GetValue(request) != null);
var propertiesWithoutValue = properties.Where(x => x.Name != firstPropertyWithValue.Name);
String.Format("{0} cannot be 0 or null when {1} is provided.",
String.Join('&', propertiesWithoutValue.Select(x => x.Name)),
firstPropertyWithValue.Name));