如何使用Fluent Validation验证数组中Stop对象的AppointmentDateTime按先后顺序递增
如何使用Fluent Validation验证数组中Stop对象的AppointmentDateTime按先后顺序递增
嗨,我完全懂你的需求——就是要确保Stops数组里的每个后续站点的预约时间都比前一个晚对吧?Fluent Validation自带的RuleForEach确实只能单个校验元素,没法直接记录前一个元素的状态,但咱们可以通过自定义集合验证规则来搞定这个问题。
其实很简单,咱们直接给整个Stops数组加一条Must规则,在规则里自己实现循环比较逻辑就行,具体代码示例如下:
// 针对整个Stops数组添加顺序验证规则 RuleFor(x => x.Stops) .Must(stops => { // 如果数组为空或者只有一个元素,直接通过验证 if (stops == null || stops.Count <= 1) return true; DateTime? previousAppointmentTime = null; foreach (var stop in stops) { // 这里其实你之前的ChildRule已经校验过时间不为空,不过加一层保险更严谨 if (stop.AppointmentDateTime == null) return false; // 对比当前站点和前一个站点的时间 if (previousAppointmentTime.HasValue && stop.AppointmentDateTime <= previousAppointmentTime) return false; // 更新前一个时间为当前时间,继续循环 previousAppointmentTime = stop.AppointmentDateTime; } return true; }) .WithMessage("站点预约时间必须按先后顺序递增,每个后续站点的时间必须晚于前一个");
如果你想让错误消息更精准,能指出具体是哪两个站点出了问题,可以稍微优化一下代码,利用Fluent Validation的消息格式化器带上索引和时间信息:
RuleFor(x => x.Stops) .Must((parentObject, stops, validationContext) => { if (stops == null || stops.Count <= 1) return true; DateTime? previousAppointmentTime = null; for (int i = 0; i < stops.Count; i++) { var currentStop = stops[i]; if (currentStop.AppointmentDateTime == null) return false; if (previousAppointmentTime.HasValue && currentStop.AppointmentDateTime <= previousAppointmentTime) { // 给消息格式化器添加参数,方便在错误消息中显示具体信息 validationContext.MessageFormatter.AppendArgument("StopIndex", i); validationContext.MessageFormatter.AppendArgument("PrevTime", previousAppointmentTime.Value.ToString("yyyy-MM-dd HH:mm:ss")); validationContext.MessageFormatter.AppendArgument("CurrentTime", currentStop.AppointmentDateTime.Value.ToString("yyyy-MM-dd HH:mm:ss")); return false; } previousAppointmentTime = currentStop.AppointmentDateTime; } return true; }) .WithMessage("索引为{StopIndex}的站点预约时间({CurrentTime})不晚于前一个站点的时间({PrevTime}),请调整顺序");
这个思路的核心就是:既然RuleForEach没法跨元素关联,那咱们就直接对整个集合做校验,在Must委托里自己实现需要的循环和状态记录逻辑,完全能满足你的需求。
备注:内容来源于stack exchange,提问作者SpaceCowboy74
相关产品推荐
相关产品推荐

