FluentValidation:将值与值的集合进行比较?

FluentValidation: Comparing a value with an aggregate of values?

在为单个 class 上的属性制定简单规则时,我发现 FluentValidation 很简单,但只要我需要比较集合中的值(例如 List<T> 属性),它变得超级多毛。假设以下两个最小 classes:

public class PurchaseOrder
{
    public List<LineItem> LineItems { get; set; }
    public decimal Total { get; set; }

    public PurchaseOrder()
    {
        LineItems = new List<LineItem>();
    }
}

public class LineItem
{
    public decimal Price { get; set; }
}

还有这个class:

class Program
{
    static void Main(string[] args)
    {
        PurchaseOrder order = new PurchaseOrder();
        order.LineItems.Add(new LineItem() { Price = 12m });
        order.LineItems.Add(new LineItem() { Price = 14m });
        order.Total = 26m;

        PurchaseOrderValidator validator = new PurchaseOrderValidator();
        ValidationResult result = validator.Validate(order);
    }
}

PurchaseOrderValidator 如何确保所有 LineItem.Price 的总和等于 PurchaseOrder.Total?这是一个存根(虽然我不确定 Must() 是要走的路):

public class PurchaseOrderValidator : AbstractValidator<PurchaseOrder>
{
    public PurchaseOrderValidator()
    {
        RuleFor(x => x.Total).Must(MatchSumOfLineItems);
    }

    private bool MatchSumOfLineItems(decimal arg)
    {
        return true;
    }
}

只需创建将 PurchaseOrder 作为输入的验证方法,然后用它做任何你想做的事情

public class PurchaseOrderValidator : AbstractValidator<PurchaseOrder>
{
    public PurchaseOrderValidator()
    {
        RuleFor(x => x).Must(MatchSumOfLineItems);
    }

    private bool MatchSumOfLineItems(PurchaseOrder arg)
    {
        return arg.Total == arg.LineItems.Sum(i => i.Price);
    }
}

或者如果你想专门为 Total 添加验证 属性 你可以使用另一个 Must 重载接受 Func<decimal, PurchaseOrder, bool>

public class PurchaseOrderValidator : AbstractValidator<PurchaseOrder>
{
    public PurchaseOrderValidator()
    {
        RuleFor(x => x.Total).Must(MatchSumOfLineItems);
    }

    private bool MatchSumOfLineItems(PurchaseOrder order, decimal sum)
    {
        return sum == order.LineItems.Sum(i => i.Price);
    }
}

为什么不用.Equal()

public class PurchaseOrderValidator : AbstractValidator<PurchaseOrder>
{
    public PurchaseOrderValidator()
    {
        RuleFor(x => x.Total).Equal(x => x.LineItems.Sum(item => item.Price));
    }
}