如何在FluentValidation的OrderDetailValidator中配置WithdrawInfoValidator
问题描述
我在API中使用FluentValidation,已创建四个验证器:
- OrderValidator(订单级验证)
- OrderDetailValidator(订单明细级验证)
- WithdrawInfoValidator(订单明细提款操作验证)
- TransferInfoValidator(订单明细转账操作验证)
我通过以下代码调用OrderValidator:
var validator = new OrderValidator(); foreach (var order in orders) { var vResult = validator.Validate(order); }
上述代码可使OrderValidator和OrderDetailValidator生效,但WithdrawInfoValidator与TransferInfoValidator无法生效,原因是我尚未在OrderDetailValidator中声明WithdrawInfoValidator。
我的问题是:如何在OrderDetailValidator中配置WithdrawInfoValidator?
原模型代码
public class Order { public string AdvisorCode { get; set; } public string AccountNo { get; set; } public string AccountType { get; set; } public List<OrderDetail> Details { get; set; } } public class OrderDetail { public int GroupNo { get; set; } public int SubOrderNo { get; set; } public string OrderType { get; set; } public string FundSource { get; set; } public string ProductCode { get; set; } public string Currency { get; set; } public decimal Amount { get; set; } public decimal Units { get; set; } public string DivOption { get; set; } public int? SellAll { get; set; } public WithdrawInfo WithdrawInfo { get; set; } } public class WithdrawInfo { public string WithdrawMode { get; set; } public string WDCurrency { get; set; } public decimal WDAmount { get; set; } public TransferInfo TransferInfo { get; set; } } public class TransferInfo { public string BankName { get; set; } public string AccountNo { get; set; } public string Branch { get; set; } public string AccHolderName { get; set; } public string SwiftCode { get; set; } public string IntBankName { get; set; } public string IntSwiftCode { get; set; } public string OtherInfoOrAddress { get; set; } } public class OrderErrorReference { public string OrderType { get; set; } public int GroupNo { get; set; } public int SubOrderNo { get; set; } }
原验证器代码
public class OrderValidator : AbstractValidator<Order> { public OrderValidator() { RuleFor(m => m.AdvisorCode) .NotEmpty() .WithMessage( "AdvisorCode field cannot be empty." ); RuleForEach(m => m.Details) .SetValidator(new OrderDetailValidator()) .When(m => m.Details != null); } } public class OrderDetailValidator : AbstractValidator<OrderDetail> { public OrderDetailValidator() { RuleFor(m => m.GroupNo) .Cascade(CascadeMode.Stop) .NotEmpty().WithMessage( "GroupNo field cannot be empty." ).WithState(m => new OrderErrorReference { GroupNo = m.GroupNo, SubOrderNo = m.SubOrderNo, OrderType = m.OrderType }) .Must(m => m > 0) .WithMessage( "GroupNo must be greater than zero." ).WithState(m => new OrderErrorReference { GroupNo = m.GroupNo, SubOrderNo = m.SubOrderNo, OrderType = m.OrderType }); } } public class WithdrawInfoValidator : AbstractValidator<WithdrawInfo> { public WithdrawInfoValidator(OrderDetail orderDetail) { RuleFor(m => m.WithdrawMode) .Cascade(CascadeMode.Stop) .Must(mode => mode == "TT" || mode == "QC") .When(m => m.WithdrawMode != null) .WithMessage( "Withdraw Mode must be either \"NA\"=No Withdrawal, \"TT\"=Telegraphic Transfer or \"QC\"=Quick Check Deposit." ).WithState(m => new OrderErrorReference { GroupNo = orderDetail.GroupNo, SubOrderNo = orderDetail.SubOrderNo, OrderType = orderDetail.OrderType }) .NotEmpty() .WithMessage( "The WithdrawMode field cannot be empty." ).WithState(m => new OrderErrorReference { GroupNo = orderDetail.GroupNo, SubOrderNo = orderDetail.SubOrderNo, OrderType = orderDetail.OrderType }); RuleFor(m => m.TransferInfo) .Must(info => info != null) .When(m => m.WithdrawMode == "TT" || m.WithdrawMode == "QC") .WithMessage( "TransferInfo cannot be empty if WithdrawMode is \"TT\" or \"QC\"" ).WithState(m => new OrderErrorReference { GroupNo = orderDetail.GroupNo, SubOrderNo = orderDetail.SubOrderNo, OrderType = orderDetail.OrderType }) .SetValidator(m => new TransferInfoValidator(orderDetail)) .When(m => m.WithdrawMode == "TT" || m.WithdrawMode == "QC"); } } public class TransferInfoValidator : AbstractValidator<TransferInfo> { public TransferInfoValidator(OrderDetail orderDetail) { RuleFor(m => m.OtherInfoOrAddress) .NotEmpty() .When(m => orderDetail.WithdrawInfo.WithdrawMode == "TT") .WithMessage( "OtherInfoOrAddress field cannot be empty if WithdrawMode is TT." ).WithState(m => new OrderErrorReference { GroupNo = orderDetail.GroupNo, SubOrderNo = orderDetail.SubOrderNo, OrderType = orderDetail.OrderType }); } }
解决方案
要让WithdrawInfoValidator和TransferInfoValidator生效,只需在OrderDetailValidator中为WithdrawInfo属性添加验证规则,通过SetValidator关联对应的验证器即可。由于你的WithdrawInfoValidator构造函数需要接收当前的OrderDetail实例,所以要使用带参数的方式创建验证器。
修改后的OrderDetailValidator代码如下:
public class OrderDetailValidator : AbstractValidator<OrderDetail> { public OrderDetailValidator() { RuleFor(m => m.GroupNo) .Cascade(CascadeMode.Stop) .NotEmpty().WithMessage( "GroupNo field cannot be empty." ).WithState(m => new OrderErrorReference { GroupNo = m.GroupNo, SubOrderNo = m.SubOrderNo, OrderType = m.OrderType }) .Must(m => m > 0) .WithMessage( "GroupNo must be greater than zero." ).WithState(m => new OrderErrorReference { GroupNo = m.GroupNo, SubOrderNo = m.SubOrderNo, OrderType = m.OrderType }); // 添加WithdrawInfo的验证规则 RuleFor(m => m.WithdrawInfo) // 传递当前OrderDetail实例给WithdrawInfoValidator .SetValidator(detail => new WithdrawInfoValidator(detail)) // 仅当WithdrawInfo不为空时触发验证,可根据业务需求调整 .When(m => m.WithdrawInfo != null); } }
关键要点:
- 关联嵌套验证器:通过
RuleFor(m => m.WithdrawInfo)指定要验证的嵌套属性,再用SetValidator绑定WithdrawInfoValidator - 传递OrderDetail实例:使用
detail => new WithdrawInfoValidator(detail)的lambda表达式,将当前的OrderDetail对象传递给WithdrawInfoValidator的构造函数,满足生成错误状态的需求 - 条件验证:
.When(m => m.WithdrawInfo != null)确保只有当WithdrawInfo存在时才执行验证,如果业务要求WithdrawInfo必须存在,可以添加Must(info => info != null)的规则并去掉该条件
修改完成后,当OrderValidator验证Order时,会自动遍历每个OrderDetail,触发OrderDetailValidator的所有规则,进而触发WithdrawInfoValidator和TransferInfoValidator的验证逻辑,实现完整的嵌套验证链路。
内容的提问来源于stack exchange,提问作者DDNL
相关产品推荐
相关产品推荐

