I decided to give this another shot, 2 years later, after seeing how many views this unanswered question had gotten. I've come up with two answers.
The first answer is the best solution for the situation described in the question.
public class SoftwareOrderValidator : AbstractValidator<SoftwareOrder>
{
public SoftwareOrderValidator()
{
RuleForEach(order => order.SoftwareItem)
.Must(BeCompatibleWithSystem)
.WithMessage("Software is incompatible with system");
}
private bool BeCompatibleWithSystem(SoftwareOrder order, SoftwareItem item)
{
if (item.Selected)
return (order.IsLegacySystem == item.IsLegacySoftware);
else
return true;
}
}
Predicate Validators (a.k.a Must) can take both object & property as arguments. This allows you to directly compare against IsLegacySystem, or any other property of the parent object.
You probably shouldn't use this second answer. If you believe you need to pass arguments into an AbstractValidator's constructor, I would encourage you to re-assess and find a different approach. With that warning said, here is one way to accomplish it.
Basically, use a dummy Must() to allow you to set a variable outside of a lambda, outside of the constructor. Then you can use that to get that value into the constructor of the second validator.
public class SoftwareOrderValidator : AbstractValidator<SoftwareOrder>
{
private bool _isLegacySystem;
public SoftwareOrderValidator()
{
RuleFor(order => order.IsLegacySystem)
.Must(SetUpSoftwareItemValidatorConstructorArg);
RuleForEach(order => order.SoftwareItem)
.SetValidator(new SoftwareItemValidator(_isLegacySystem));
}
private bool SetUpSoftwareItemValidatorConstructorArg(bool isLegacySystem)
{
_isLegacySystem = isLegacySystem;
return true;
}
}
public class SoftwareItemValidator : AbstractValidator<SoftwareItem>
{
public SoftwareItemValidator(bool IsLegacySystem)
{
When(item => item.Selected, () =>
{
RuleFor(item => item.IsLegacySoftware)
.Equal(IsLegacySystem).WithMessage("Software is incompatible with system");
});
}
}