Reputation: 381
I find FluentValidation straightforward when making simple rules for properties on a single class, but as soon as I need to compare values from collections (e.g. a List<T>
property), it gets super-hairy. Assume the follwing two minimal 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; }
}
And this 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);
}
}
How would PurchaseOrderValidator
look like to make sure that the sum of all LineItem.Price
equals the PurchaseOrder.Total
? Here's a stub (although I'm not sure Must()
is the way to go):
public class PurchaseOrderValidator : AbstractValidator<PurchaseOrder>
{
public PurchaseOrderValidator()
{
RuleFor(x => x.Total).Must(MatchSumOfLineItems);
}
private bool MatchSumOfLineItems(decimal arg)
{
return true;
}
}
Upvotes: 2
Views: 2302
Reputation: 4445
Why not use .Equal()
public class PurchaseOrderValidator : AbstractValidator<PurchaseOrder>
{
public PurchaseOrderValidator()
{
RuleFor(x => x.Total).Equal(x => x.LineItems.Sum(item => item.Price));
}
}
Upvotes: 6
Reputation: 9632
Just create validation method that takes PurchaseOrder
as input and do whatever you want with it
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);
}
}
or if you want to add validation specifically for Total
property you can use another Must
overload accepts 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);
}
}
Upvotes: 5