You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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);
    }
}

关键要点:

  1. 关联嵌套验证器:通过RuleFor(m => m.WithdrawInfo)指定要验证的嵌套属性,再用SetValidator绑定WithdrawInfoValidator
  2. 传递OrderDetail实例:使用detail => new WithdrawInfoValidator(detail)的lambda表达式,将当前的OrderDetail对象传递给WithdrawInfoValidator的构造函数,满足生成错误状态的需求
  3. 条件验证:.When(m => m.WithdrawInfo != null)确保只有当WithdrawInfo存在时才执行验证,如果业务要求WithdrawInfo必须存在,可以添加Must(info => info != null)的规则并去掉该条件

修改完成后,当OrderValidator验证Order时,会自动遍历每个OrderDetail,触发OrderDetailValidator的所有规则,进而触发WithdrawInfoValidator和TransferInfoValidator的验证逻辑,实现完整的嵌套验证链路。


内容的提问来源于stack exchange,提问作者DDNL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 21:30:56