| File: DataAnnotationsModelValidatorProvider.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.DataAnnotations\src\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj (Microsoft.AspNetCore.Mvc.DataAnnotations) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Mvc.DataAnnotations; /// <summary> /// An implementation of <see cref="IModelValidatorProvider"/> which provides validators /// for attributes which derive from <see cref="ValidationAttribute"/>. It also provides /// a validator for types which implement <see cref="IValidatableObject"/>. /// </summary> internal sealed class DataAnnotationsModelValidatorProvider : IMetadataBasedModelValidatorProvider { private readonly IOptions<MvcDataAnnotationsLocalizationOptions> _options; private readonly IStringLocalizerFactory? _stringLocalizerFactory; private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider; /// <summary> /// Create a new instance of <see cref="DataAnnotationsModelValidatorProvider"/>. /// </summary> /// <param name="validationAttributeAdapterProvider">The <see cref="IValidationAttributeAdapterProvider"/> /// that supplies <see cref="IAttributeAdapter"/>s.</param> /// <param name="options">The <see cref="IOptions{MvcDataAnnotationsLocalizationOptions}"/>.</param> /// <param name="stringLocalizerFactory">The <see cref="IStringLocalizerFactory"/>.</param> /// <remarks><paramref name="options"/> and <paramref name="stringLocalizerFactory"/> /// are nullable only for testing ease.</remarks> public DataAnnotationsModelValidatorProvider( IValidationAttributeAdapterProvider validationAttributeAdapterProvider, IOptions<MvcDataAnnotationsLocalizationOptions> options, IStringLocalizerFactory? stringLocalizerFactory) { ArgumentNullException.ThrowIfNull(validationAttributeAdapterProvider); ArgumentNullException.ThrowIfNull(options); _validationAttributeAdapterProvider = validationAttributeAdapterProvider; _options = options; _stringLocalizerFactory = stringLocalizerFactory; } public void CreateValidators(ModelValidatorProviderContext context) { IStringLocalizer? stringLocalizer = null; if (_stringLocalizerFactory != null && _options.Value.DataAnnotationLocalizerProvider != null) { stringLocalizer = _options.Value.DataAnnotationLocalizerProvider( context.ModelMetadata.ContainerType ?? context.ModelMetadata.ModelType, _stringLocalizerFactory); } var results = context.Results; // Read interface .Count once rather than per iteration var resultsCount = results.Count; for (var i = 0; i < resultsCount; i++) { var validatorItem = results[i]; if (validatorItem.Validator != null) { continue; } if (!(validatorItem.ValidatorMetadata is ValidationAttribute attribute)) { continue; } var validator = new DataAnnotationsModelValidator( _validationAttributeAdapterProvider, attribute, stringLocalizer); validatorItem.Validator = validator; validatorItem.IsReusable = true; // Inserts validators based on whether or not they are 'required'. We want to run // 'required' validators first so that we get the best possible error message. if (attribute is RequiredAttribute) { context.Results.Remove(validatorItem); context.Results.Insert(0, validatorItem); } } // Produce a validator if the type supports IValidatableObject if (typeof(IValidatableObject).IsAssignableFrom(context.ModelMetadata.ModelType)) { context.Results.Add(new ValidatorItem { Validator = new ValidatableObjectAdapter(), IsReusable = true }); } } public bool HasValidators(Type modelType, IList<object> validatorMetadata) { if (typeof(IValidatableObject).IsAssignableFrom(modelType)) { return true; } // Read interface .Count once rather than per iteration var validatorMetadataCount = validatorMetadata.Count; for (var i = 0; i < validatorMetadataCount; i++) { if (validatorMetadata[i] is ValidationAttribute) { return true; } } return false; } }