1

Closed

Make inheritance of FluentValidationModelValidatorProvider useful

description

Hi Jeremy,

The FluentValidationModelValidatorProvider integration with MVC is very useful. However, if one would like to override some parts of the class it is not possible since the only overridable function is GetValidators.

In particular, it would be interesting to be able to access the (currently) private method IsValidatingProperty and create a new method that invokes the validator factory, which can be overriden.

Rationale: The type passed to the validator factory may not be always correct in order to make the best decision for a validator. In other words, ModelMetadata.ContainerType or ModelMetadata.ModelType (depending on wether a property or the model is being validated) may not be the correct type to use.

For example, suppose the following form models:
class F1 { 
    int Property {get; set;}
}

class F2 : F1 {
   int Another { get; set; }
}
Each with its own validator. Now suppose I have a partial view that renders the appropriate way to select Property (involving a TextBoxFor(f => f.Property), and it is strongly typed to F1. This partial will be called from a strongly typed view of F2.

In this scenario, when the TextBoxFor(f => f.Property) is rendered, the ModelMetadata.ContainerType will be F1 instead of F2, thus rendering the wrong unobtrusive validations.

Summary: an overridable function that determines which type to pass to the validator factory would be nice to have in the Provider.


The following code should provide everything needed.
public virtual IValidator InvokeValidatorFactory(ModelMetadata metadata,
                                             ControllerContext context) {
    if (IsValidatingProperty(metadata)) {
        return ValidatorFactory.GetValidator(metadata.ContainerType);
    }
    return ValidatorFactory.GetValidator(metadata.ModelType);
}
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, 
                                                ControllerContext context) {
    IValidator validator = InvokeValidatorFactory(metadata, context);
    if (IsValidatingProperty(metadata)) {
        return GetValidatorsForProperty(metadata, context, validator);
    }
    return GetValidatorsForModel(metadata, context, validator);
}
Closed Apr 23, 2013 at 1:20 PM by JeremyS

comments

JeremyS wrote Mar 28, 2013 at 3:06 PM

I've implemented this in the latest commit (although I called the method "CreateValidator" rather than "InvokeValidatorFactory" for consistency with other areas of the codebase). Please give this a try and let me know if this works for you.

Jeremy

fsateler wrote Mar 28, 2013 at 4:33 PM

Looks good. I will test it later today. However, the code I suggested (which you seem to have adapted) missed making IsValidatingProperty protected, so that we could do the same check in a derived class.

fsateler wrote Apr 29, 2013 at 11:19 PM

Just for the record, the current FluentValidation 4.0 fixes this problem for me.

Thanks for your work in this!