I needed something similiar for one-to-one properties that could be of several subclass types so I adapted your PolymorphicCollectionValidator to a PolymorphicValidator and it seems to be working great.
An example of the situation I needed this for is:
A "BillingInfo" object has a "PaymentInfo" property.
The PaymentInfo property may contain an instance of "CreditCardPaymentInfo" or "DirectDebitPaymentInfo", both of which derive from "PaymentInfo".
Here's the class I adapted to meet these needs:
Then in the BillingInfoValidator, I set the PaymentInfo property's validator to the new PolymorphicValidator like so: (Yes, I could have done it more "fluently", but for readability sake, I went one line at a time to show how each validator is registered.)
An example of the situation I needed this for is:
A "BillingInfo" object has a "PaymentInfo" property.
The PaymentInfo property may contain an instance of "CreditCardPaymentInfo" or "DirectDebitPaymentInfo", both of which derive from "PaymentInfo".
Here's the class I adapted to meet these needs:
public class PolymorphicValidator<TBaseClass> : NoopPropertyValidator
{
Dictionary<Type, IValidator> derivedValidators = new Dictionary<Type, IValidator>();
IValidator<TBaseClass> baseValidator;
public PolymorphicValidator(IValidator<TBaseClass> baseValidator)
{
this.baseValidator = baseValidator;
}
public PolymorphicValidator<TBaseClass> Add<TDerived>(IValidator<TDerived> derivedValidator) where TDerived : TBaseClass
{
derivedValidators[typeof(TDerived)] = derivedValidator;
return this;
}
public override IEnumerable<ValidationFailure> Validate(PropertyValidatorContext context)
{
var objToValidate = context.PropertyValue;
// bail out if the property is null
if (objToValidate == null) return Enumerable.Empty<ValidationFailure>();
// get the first element out of the collection and check its real type.
var actualType = objToValidate.GetType();
IValidator derivedValidator;
ChildValidatorAdaptor childValidator;
if (derivedValidators.TryGetValue(actualType, out derivedValidator))
{
// we found a validator for the specific subclass.
childValidator = new ChildValidatorAdaptor(derivedValidator);
return childValidator.Validate(context);
}
// Otherwise fall back to the validator for the base class.
childValidator = new ChildValidatorAdaptor(baseValidator);
return childValidator.Validate(context);
}
}
The validators for PaymentInfo, CreditCardPaymentInfo and DirectDebitPaymentInfo were all created just like the "Person, Employee, Member" example above.Then in the BillingInfoValidator, I set the PaymentInfo property's validator to the new PolymorphicValidator like so: (Yes, I could have done it more "fluently", but for readability sake, I went one line at a time to show how each validator is registered.)
var paymentInfoValidator = new PolymorphicValidator<PaymentInfo>(new PaymentInfoBaseValidator<PaymentInfo>());
paymentInfoValidator.Add<CreditCardPaymentInfo>(new CreditCardPaymentInfoValidator());
paymentInfoValidator.Add<DirectDebitPaymentInfo>(new DirectDebitPaymentInfoValidator());
RuleFor(e => e.PaymentInfo).SetValidator(paymentInfoValidator);