Support IServiceProvider Injection for DataAnnotations validations through DependencyResolver

Topics: ASP.NET Web API
Nov 2, 2012 at 1:08 PM

I opened up the following issue recently: http://aspnetwebstack.codeplex.com/workitem/463

I am not sure if it will be implemented so I decided to dig deeper and implement it by myself but that attempt was a failure :s Validator provider extensibility point seems complicated to me. However, I am sure there are good reasons why that part has been structured that way.

What is the most elegant way to implement this feature by providing a new validator provider? Would it be better to override the some parts of DataAnnotationsmodelValidatorProvider? Or should we follow a way where we will create a brand new provider?

Nov 5, 2013 at 7:22 PM
I am trying to implement this feature.
If found two possibilities :
Provide the IServiceProvider to the DataAnnotationsModelValidator by passing it to the ctor :

DataAnnotationsModelValidatorProvider.cs

protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<ModelValidatorProvider> validatorProviders, IEnumerable<Attribute> attributes)
        {
[...]
            // Produce a validator for each validation attribute we find
            foreach (ValidationAttribute attribute in attributes.OfType<ValidationAttribute>())
            {
                DataAnnotationsModelValidationFactory factory;
                if (!AttributeFactories.TryGetValue(attribute.GetType(), out factory))
                {
                    factory = DefaultAttributeFactory;
                }
                results.Add(factory(validatorProviders, attribute, serviceProvider));
            }
[...]
     }
Or provide the IServiceProvider to the DataAnnotationsModelValidator by passing it to a property :
protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<ModelValidatorProvider> validatorProviders, IEnumerable<Attribute> attributes)
        {
[...]
            // Produce a validator for each validation attribute we find
            foreach (ValidationAttribute attribute in attributes.OfType<ValidationAttribute>())
            {
                DataAnnotationsModelValidationFactory factory;
                if (!AttributeFactories.TryGetValue(attribute.GetType(), out factory))
                {
                    factory = DefaultAttributeFactory;
                }
                var validator = factory(validatorProviders, attribute);
                DataAnnotationsModelValidator dataAnnotationsModelValidator = validator as DataAnnotationsModelValidator;
                if (dataAnnotationsModelValidator != null)
                {
                    dataAnnotationsModelValidator.ServiceProvider = serviceProvider;
                }
                
                results.Add(validator);
            }
[...]
         }
The first solution seems to be the best, but it implies the modification of the public delegate DataAnnotationsModelValidationFactory :
    // A factory for validators based on ValidationAttribute
    public delegate ModelValidator DataAnnotationsModelValidationFactory(IEnumerable<ModelValidatorProvider> validatorProviders, ValidationAttribute attribute, IServiceProvider serviceProvider);
A default value could be provided to the delegate :
    // A factory for validators based on ValidationAttribute
    public delegate ModelValidator DataAnnotationsModelValidationFactory(IEnumerable<ModelValidatorProvider> validatorProviders, ValidationAttribute attribute, IServiceProvider serviceProvider = null);
The second solution is not very nice, however it does not modify public member.

Any advice ?