示例#1
0
        /// <summary>
        /// 要驗證的結構描述。
        /// </summary>
        /// <param name="relation">相關要驗證的元件。</param>
        /// <param name="source">主要驗證的元件。</param>
        /// <returns>驗證的訊息內容。</returns>
        public static IEnumerable <string> Validate(object relation, object source)
        {
            foreach (PropertyInfo propInfo in source.GetType().GetProperties())
            {
                object[] customAttributes = propInfo.GetCustomAttributes(typeof(ValidationAttribute), inherit: true);

                foreach (object customAttribute in customAttributes)
                {
                    ValidationAttribute validationAttribute = (ValidationAttribute)customAttribute;

                    bool isValid = false;
                    // 預設驗證的 Attributes。
                    if (validationAttribute.GetType() == typeof(RequiredAttribute) || validationAttribute.GetType() == typeof(RangeAttribute) ||
                        validationAttribute.GetType() == typeof(RegularExpressionAttribute) || validationAttribute.GetType() == typeof(StringLengthAttribute))
                    {
                        isValid = validationAttribute.IsValid(propInfo.GetValue(source, BindingFlags.GetProperty, null, null, null));
                    }
                    // 自訂驗證的 Attributes。
                    else
                    {
                        isValid = validationAttribute.IsValid(new object[] { propInfo.Name, propInfo.GetValue(source, BindingFlags.GetProperty, null, null, null), source, relation });
                    }

                    if (!isValid)
                    {
                        yield return(validationAttribute.FormatErrorMessage(propInfo.Name));
                    }
                }
            }
        }
示例#2
0
        private static void ValidAttribute(object item, PropertyInfo detailPropInfo, object customAttribute,
                                           out ValidationAttribute validationAttribute, out bool isValid)
        {
            validationAttribute = (ValidationAttribute)customAttribute;

            isValid = false;
            // 預設驗證的 Attributes。
            if (validationAttribute.GetType() == typeof(RequiredAttribute) || validationAttribute.GetType() ==
                typeof(RangeAttribute) ||
                validationAttribute.GetType() ==
                typeof(RegularExpressionAttribute) ||
                validationAttribute.GetType() ==
                typeof(StringLengthAttribute))
            {
                isValid = validationAttribute.IsValid(detailPropInfo.GetValue(item, BindingFlags.GetProperty, null,
                                                                              null, null));
            }

            // 自訂驗證的 Attributes。
            else
            {
                isValid = validationAttribute.IsValid(new object[]
                {
                    detailPropInfo.Name, detailPropInfo.GetValue(item, BindingFlags.GetProperty, null, null, null), item
                });
            }
        }
示例#3
0
        /// <summary>
        /// Get default message if the localized string is missing
        /// </summary>
        /// <param name="metadata">Model meta data</param>
        /// <param name="attr">Attribute to translate</param>
        /// <returns>Formatted message</returns>
        protected virtual string GetMissingTranslationMessage(ModelMetadata metadata, ValidationAttribute attr)
        {
            _logger.Warning("Failed to find translation for " + attr.GetType().Name + " on " +
                            metadata.ContainerType + "." + metadata.PropertyName);


            return(string.Format("[{0}: {1}]", CultureInfo.CurrentUICulture.Name,
                                 attr.GetType().Name.Replace("Attribute", "")));
        }
示例#4
0
        /// <summary>
        /// Generated ko validation rule based on ValidationAttribute
        /// </summary>
        /// <param name="validationAttribute"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static string GenerateRuleIfRegistered(ValidationAttribute validationAttribute, string propertyName)
        {
            if (_container.ContainsKey(validationAttribute.GetType()))
            {
                return((_container[validationAttribute.GetType()]).GenerateClientValidationRule(validationAttribute, propertyName));
            }

            return(string.Empty);
        }
        internal static string BuildResourceKey(string keyPrefix, ValidationAttribute attribute)
        {
            var result = $"{keyPrefix}-{attribute.GetType().Name.Replace("Attribute", string.Empty)}";

            if (attribute.GetType().IsAssignableFrom(typeof(DataTypeAttribute)))
            {
                result += ((DataTypeAttribute)attribute).DataType;
            }

            return(result);
        }
示例#6
0
        // <summary>
        // Validates a property or an entity.
        // </summary>
        // <param name="entityValidationContext"> Validation context. Never null. </param>
        // <param name="property"> Property to validate. Null for entity validation. Not null for property validation. </param>
        // <returns>
        // Validation errors as <see cref="IEnumerable{DbValidationError}" /> . Empty if no errors, never null.
        // </returns>
        public virtual IEnumerable <DbValidationError> Validate(
            EntityValidationContext entityValidationContext, InternalMemberEntry property)
        {
            DebugCheck.NotNull(entityValidationContext);

            var validationContext = entityValidationContext.ExternalValidationContext;

            validationContext.SetDisplayName(property, _displayAttribute);

            var objectToValidate = property == null
                                       ? entityValidationContext.InternalEntity.Entity
                                       : property.CurrentValue;

            ValidationResult validationResult = null;

            try
            {
                validationResult = _validationAttribute.GetValidationResult(objectToValidate, validationContext);
            }
            catch (Exception ex)
            {
                throw new DbUnexpectedValidationException(
                          Strings.DbUnexpectedValidationException_ValidationAttribute(
                              validationContext.DisplayName, _validationAttribute.GetType()),
                          ex);
            }

            return(validationResult != ValidationResult.Success
                       ? DbHelpers.SplitValidationResults(validationContext.MemberName, new[] { validationResult })
                       : Enumerable.Empty <DbValidationError>());
        }
示例#7
0
        public static ValidationType GetValidationType(this ValidationAttribute attribute)
        {
            _ = attribute ?? throw new ArgumentNullException(nameof(attribute));

            switch (attribute.GetType().Name)
            {
            case nameof(RequiredAttribute): return(ValidationType.Required);

            case nameof(DataTypeAttribute): return(ValidationType.DataType);

            case nameof(StringLengthAttribute): return(ValidationType.StringLength);

            case nameof(RangeAttribute): return(ValidationType.Range);

            case nameof(RegularExpressionAttribute): return(ValidationType.RegularExpression);

            case nameof(CannotEqualAttribute): return(ValidationType.CannotEqual);

            case nameof(CollectionRangeAttribute): return(ValidationType.CollectionRange);

            case nameof(ColorAttribute): return(ValidationType.Color);

            case nameof(DateGreaterOrEqualThanAttribute): return(ValidationType.DateGreaterOrEqualThan);

            case nameof(DateTimeAttribute): return(ValidationType.DateTime);

            case nameof(DecimalGreaterThanZeroAttribute): return(ValidationType.DecimalGreaterThanZero);

            case nameof(EmailAttribute): return(ValidationType.EmailAddress);

            case nameof(EmailCollectionAttribute): return(ValidationType.EmailCollection);

            case nameof(MinCollectionLengthAttribute): return(ValidationType.MinCollectionLength);

            case nameof(NotEmptyStringAttribute): return(ValidationType.NotEmptyString);

            case nameof(NullableForeignKeyAttribute): return(ValidationType.NullableForeignKey);

            case nameof(RequiredIfAttribute): return(ValidationType.RequiredIf);

            case nameof(RequiredIfHasValueAttribute): return(ValidationType.RequiredIfHasValue);

            case nameof(RequiredNonDefaultAttribute): return(ValidationType.RequiredNonDefault);

            case nameof(IdentificationNumberAttribute): return(ValidationType.IdentificationNumber);

            case nameof(IdentificationNumberPatchAttribute): return(ValidationType.IdentificationNumber);

            case nameof(NumericSequenceNumberFormatAttribute): return(ValidationType.NumericSequenceNumberFormat);

            case nameof(BankAccountNumberAttribute): return(ValidationType.BankAccountNumber);

            case nameof(IbanAttribute): return(ValidationType.Iban);

            case nameof(MinPasswordStrengthAttribute): return(ValidationType.MinPasswordStrength);

            default:
                throw new NotImplementedException($"{nameof(ValidationType)} doesn't contain value for {attribute.GetType().Name}.");
            }
        }
示例#8
0
 public void CreateValidationMetadata(
     ValidationMetadataProviderContext context)
 {
     if (context.Key.ModelType.GetTypeInfo().IsValueType&&
         context.ValidationMetadata.ValidatorMetadata
         .Where(m => m.GetType() == typeof(RequiredAttribute)).Count() == 0)
     {
         context.ValidationMetadata.ValidatorMetadata.
         Add(new RequiredAttribute());
     }
     foreach (var attribute in context.ValidationMetadata.ValidatorMetadata)
     {
         ValidationAttribute tAttr = attribute as ValidationAttribute;
         if (tAttr != null /*&& tAttr.ErrorMessage == null */ && tAttr.ErrorMessageResourceName == null)
         {
             var name = tAttr.GetType().Name;
             if (resourceManager.GetString(name) != null)
             {
                 tAttr.ErrorMessageResourceType = resourceType;
                 tAttr.ErrorMessageResourceName = name;
                 tAttr.ErrorMessage             = null;
             }
         }
     }
 }
示例#9
0
        public static string MapAttributeToConstant(ValidationAttribute attribute)
        {
            switch (attribute)
            {
            case RequiredAttribute:
                return(NotBlankError);

            case EmailAddressAttribute:
                return(InvalidEmailError);

            case UniqueEmailAttribute:
            case UniqueNickNameAttribute:
                return(NotUniqueError);

            case StringLengthAttribute:
            case MinLengthAttribute:
            case MaxLengthAttribute:
                return(LengthError);

            case RangeAttribute:
                return(RangeError);

            default:
                throw new ArgumentException("Attribute not implemented", attribute.GetType().Name);
            }
        }
示例#10
0
            public void CreateValidationMetadata(
                ValidationMetadataProviderContext context)
            {
                if (context.Key.ModelType.GetTypeInfo().IsValueType&&
                    context.ValidationMetadata.ValidatorMetadata.Where(m => m.GetType() == typeof(RequiredAttribute)).Count() == 0)
                {
                    context.ValidationMetadata.ValidatorMetadata.Add(new RequiredAttribute());
                }

                foreach (var attribute in context.ValidationMetadata.ValidatorMetadata)
                {
                    ValidationAttribute tAttr = attribute as ValidationAttribute;
                    if (tAttr != null && tAttr.ErrorMessageResourceName == null)
                    {
                        //何故かEmailAddressAttributeはErrorMessageがデフォルトでnullにならない。
                        if (tAttr.ErrorMessage == null || (attribute as EmailAddressAttribute != null && !string.IsNullOrEmpty(tAttr.ErrorMessage)))
                        {
                            var name = tAttr.GetType().Name;
                            if (resourceManager.GetString(name) != null)
                            {
                                tAttr.ErrorMessageResourceType = resourceType;
                                tAttr.ErrorMessageResourceName = name;
                                tAttr.ErrorMessage             = null;
                            }
                        }
                    }
                }
            }
示例#11
0
        public void Validate_IsValidFalse_StringLocalizerGetsArguments(
            ValidationAttribute attribute,
            string model,
            object[] values)
        {
            // Arrange
            var stringLocalizer = new Mock <IStringLocalizer>();

            var validator = new DataAnnotationsModelValidator(
                new ValidationAttributeAdapterProvider(),
                attribute,
                stringLocalizer.Object);

            var metadata          = _metadataProvider.GetMetadataForType(typeof(SampleModel));
            var validationContext = new ModelValidationContext(
                actionContext: new ActionContext(),
                modelMetadata: metadata,
                metadataProvider: _metadataProvider,
                container: null,
                model: model);

            // Act
            validator.Validate(validationContext);

            // Assert
            var json = Newtonsoft.Json.JsonConvert.SerializeObject(values) + " " + attribute.GetType().Name;

            stringLocalizer.Verify(l => l[LocalizationKey, values], json);
        }
        private static string GetValidationAttributeType(
            ValidationAttribute validationAttribute,
            out int?validationValue)
        {
            validationValue = null;

            var typeName = validationAttribute
                           .GetType()
                           .Name
                           .Replace("Attribute", string.Empty);

            var firstChar = char.ToLowerInvariant(typeName[0]);

            var attributeType = $"{firstChar}{typeName.Substring(1)}";

            if (validationAttribute is MinLengthAttribute)
            {
                validationValue = ((MinLengthAttribute)validationAttribute).Length;
            }
            else if (validationAttribute is MaxLengthAttribute)
            {
                validationValue = ((MaxLengthAttribute)validationAttribute).Length;
            }

            if (attributeType == "emailAddress")
            {
                return("email");
            }

            return(attributeType);
        }
示例#13
0
        /// <summary>
        /// Creates a <see cref="ValidatorData"/> instance from <paramref name="validationAttribute"/>.
        /// </summary>
        /// <param name="validationAttribute"></param>
        /// <returns>
        /// <see cref="ValidatorData"/> equivlent of <paramref name="validationAttribute"/>.
        /// </returns>
        public virtual ValidatorData BuildDynamicControlValidatorData(ValidationAttribute validationAttribute)
        {
            Type validationAttributeType = validationAttribute.GetType();

            string name         = char.ToLower(validationAttributeType.Name[0]) + validationAttributeType.Name.Replace("Attribute", "").Substring(1);
            string errorMessage = validationAttribute.
                                  ErrorMessageResourceType.
                                  GetProperty(validationAttribute.ErrorMessageResourceName).
                                  GetValue(null, null) as string;

            List <PropertyInfo> propertyInfos = validationAttributeType.
                                                GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly).ToList <PropertyInfo>();

            Dictionary <string, string> options = new Dictionary <string, string>();

            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                // Use ToString here since value may not be a string
                options.Add(propertyInfo.Name,
                            propertyInfo.GetValue(validationAttribute).ToString());
            }
            return(new ValidatorData()
            {
                Name = name,
                ErrorMessage = errorMessage,
                Options = options
            });
        }
        /// <summary>
        /// Create client validation rules for Data Annotation attributes.
        /// </summary>
        /// <param name="attribute">Attribute</param>
        /// <param name="errorMessage">Not formatted error message (should contain {0} etc}</param>
        /// <returns>A collection of rules (or an empty collection)</returns>
        public virtual IEnumerable<ModelClientValidationRule> Create(ValidationAttribute attribute, string errorMessage)
        {
            IValidationAttributeAdapterFactory factory;
            if (!_factories.TryGetValue(attribute.GetType(), out factory))
                return new ModelClientValidationRule[0];

            return factory.Create(attribute, errorMessage);
        }
示例#15
0
        private static ExecutionError CreateExecutionError(ValidationAttribute attribute, ValidationResult validationResult)
        {
            var errorCode     = AnnotationErrorCodes.GetErrorCodeFromAttribute(attribute.GetType());
            var errorCodeInfo = new ErrorCodeInfo(errorCode, validationResult.ErrorMessage);
            var source        = validationResult.MemberNames.FirstOrDefault();

            return(new ExecutionError(errorCodeInfo, source, validationResult.ErrorMessage));
        }
        public ModelProperty <TModelType> Include(ValidationAttribute inclusion)
        {
            if (!this.Specification.Inclusion.Any(x => x.GetType() == inclusion.GetType()))
            {
                this.Specification.Inclusion.Add(inclusion);
            }

            return(this);
        }
示例#17
0
        internal override void GenerateRulesetBase(ValidationAttribute attribute, ModelExplorer model, RulesetBuilder ruleset)
        {
            if (attribute as T == null)
            {
                throw new InvalidCastException($"Type {attribute.GetType()} cannot be casted to {typeof(T)}");
            }

            this.GenerateRuleset(attribute as T, model, ruleset);
        }
        private string GetValidationPropertyAlias(ValidationAttribute attribute, string propertyName)
        {
            const string prefix = "validation";

            if (attribute is RegularExpressionAttribute || attribute is EmailAddressAttribute)
            {
                return(string.Concat(prefix, "InvalidFormat", propertyName));
            }
            return(string.Concat(prefix, attribute.GetType().Name.Replace("Attribute", string.Empty), propertyName));
        }
        private string GetValidationPropertyName(ValidationAttribute attribute, string propertyName)
        {
            propertyName = propertyName.UppercaseFirst();
            if (attribute is RegularExpressionAttribute || attribute is EmailAddressAttribute)
            {
                return("InvalidFormat " + propertyName);
            }

            return(string.Concat(attribute.GetType().Name.Replace("Attribute", string.Empty), " ", propertyName));
        }
示例#20
0
                static string GetAttributeName(ValidationAttribute attribute)
                {
                    var          type           = attribute.GetType();
                    var          type_name      = type.Name;
                    const string attribute_name = "Attribute";

                    return(type_name.EndsWith(attribute_name, StringComparison.InvariantCulture)
                        ? type_name.Substring(0, type_name.Length - attribute_name.Length)
                        : type_name);
                }
        public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
        {
            attribute.ErrorMessage = "Validation_" + attribute.GetType().Name.Replace("Attribute", string.Empty);

            // You might need this if you have custom DataTypeAttribute, for us this just creates
            // "EmailAddress_EmailAddress" instead of "EmailAddress" as key.
            //if (attribute is DataTypeAttribute dataTypeAttribute)
            //    attribute.ErrorMessage += "_" + dataTypeAttribute.DataType;

            return(_originalProvider.GetAttributeAdapter(attribute, stringLocalizer));
        }
示例#22
0
        public IAttributeAdapter?GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer localizer)
        {
            Type type = attribute.GetType();

            if (type == typeof(RequiredAttribute))
            {
                return(new RequiredAdapter((RequiredAttribute)attribute));
            }

            return(null);
        }
示例#23
0
        /// <summary>
        /// Gets the error message for the specified attribute.
        /// </summary>
        public virtual string GetErrorMessage(ValidationAttribute attribute, string propertyDisplayName)
        {
            var attributeName = attribute.GetType().Name;

            if (attributeName.EndsWith("Attribute", StringComparison.CurrentCultureIgnoreCase))
            {
                attributeName = attributeName.Substring(0, attributeName.Length - "Attribute".Length);
            }

            return(string.Format(GetDefaultErrorMessagePattern(attributeName), propertyDisplayName));
        }
 public IValidationRule FindRule(ValidationAttribute attr)
 {
     foreach (var attribute in attr.GetType().GetCustomAttributes(true))
     {
         var ruleMapping = attribute as RuleMappingAttribute;
         if (ruleMapping != null)
         {
             return (IValidationRule)Activator.CreateInstance(ruleMapping.RuleType);
         }
     }
     return null;
 }
示例#25
0
        internal static ValidationAttribute GetValidatorInstanceForXmlDataAnnotationsValidator(XmlDataAnnotationsValidator validator)
        {
            if (validator == null)
            {
                return(null);
            }
            ValidationAttribute attr = null;

            if (validator.ValidatorType == typeof(StringLengthAttribute))
            {
                attr = (ValidationAttribute) new StringLengthAttribute(int.Parse(validator.ValidatorAttributes["maximumLength"]));
            }
            else if (validator.ValidatorType == typeof(RangeAttribute))
            {
                attr = (ValidationAttribute) new RangeAttribute(double.Parse(validator.ValidatorAttributes["minimum"]), double.Parse(validator.ValidatorAttributes["maximum"]));
            }
            else if (validator.ValidatorType == typeof(RegularExpressionAttribute))
            {
                attr = (ValidationAttribute) new RegularExpressionAttribute(validator.ValidatorAttributes["pattern"]);
            }
            else
            {
                try {
                    attr = (ValidationAttribute)Activator.CreateInstance(validator.ValidatorType);
                } catch (Exception ex) {
                    throw new Exception("Error trying to create an instance of [" + validator.ValidatorType.Name + "].\r\nIf this validator already has a parameterless constructor defined, see InnerException for more information.", ex);
                }
                string currentProperty = "";
                try {
                    foreach (KeyValuePair <string, string> kvp in validator.ValidatorAttributes)
                    {
                        currentProperty = kvp.Key;
                        PropertyInfo pi = attr.GetType().GetProperty(kvp.Key);
                        pi.SetValue(attr, Convert.ChangeType(kvp.Value, pi.PropertyType), null);
                    }
                } catch (Exception ex) {
                    throw new Exception("Error setting properties from XML configuration, current property [" + currentProperty + "]. InnerException may have more details.", ex);
                }
            }
            if (!String.IsNullOrEmpty(validator.ErrorMessage))
            {
                attr.ErrorMessage = validator.ErrorMessage;
            }
            if (!String.IsNullOrEmpty(validator.ErrorMessageResourceName))
            {
                attr.ErrorMessageResourceName = validator.ErrorMessageResourceName;
            }
            if (validator.ErrorMessageResourceType != null)
            {
                attr.ErrorMessageResourceType = validator.ErrorMessageResourceType;
            }
            return(attr);
        }
        private static string GetRemoteValidatorName(ValidationAttribute attribute)
        {
            if (routeDataProp == null)
            {
                var type = attribute.GetType();
                routeDataProp = type.GetTypeInfo().GetDeclaredProperty("RouteData");
            }

            var routeData = (IDictionary <string, object>)routeDataProp.GetValue(attribute);

            return(routeData["action"]?.ToString());
        }
示例#27
0
        public void AdapterFactory_RegistersAdapters_ForDataAnnotationAttributes(ValidationAttribute attribute,
                                                                                 Type expectedAdapterType)
        {
            // Arrange
            var adapters       = new DataAnnotationsModelValidatorProvider().AttributeFactories;
            var adapterFactory = adapters.Single(kvp => kvp.Key == attribute.GetType()).Value;

            // Act
            var adapter = adapterFactory(attribute);

            // Assert
            Assert.IsType(expectedAdapterType, adapter);
        }
        private ValidationAttribute CopyAttribute(ValidationAttribute attribute)
        {
            ValidationAttribute result = null;

            if (attribute is RangeAttribute)
            {
                var attr = (RangeAttribute)attribute;
                result = (attr.Minimum is double)
                                        ?  new RangeAttribute((double)attr.Minimum, (double)attr.Maximum)
                                        :  new RangeAttribute((int)attr.Minimum, (int)attr.Maximum);
            }

            if (attribute is RegularExpressionAttribute)
            {
                var attr = (RegularExpressionAttribute)attribute;
                result = new RegularExpressionAttribute(attr.Pattern);
            }

            if (attribute is RequiredAttribute)
            {
                result = new RequiredAttribute();
            }

            if (attribute is StringLengthAttribute)
            {
                var attr = (StringLengthAttribute)attribute;
                result = new StringLengthAttribute(attr.MaximumLength)
                {
                    MinimumLength = attr.MinimumLength
                };
            }

            if (attribute is DA.CompareAttribute)
            {
                var attr = (DA.CompareAttribute)attribute;
                result = new DA.CompareAttribute(attr.OtherProperty);
            }

            if (attribute is DataTypeAttribute)
            {
                var attr = (DataTypeAttribute)attribute;
                result = new DataTypeAttribute(attr.DataType);
            }

            if (result == null && attribute.GetType().GetInterfaces().Contains(typeof(ICloneable)))
            {
                result = ((ICloneable)attribute).Clone() as ValidationAttribute;
            }

            return(result);
        }
        protected virtual void HandleErrorMessage(Type type, string propertyName, ValidationAttribute attribute)
        {
            var languageKey = GetErrorMessageLanguageKey(type, propertyName, attribute.GetType().Name);

            if (Translator.TranslationExists(languageKey) && OverrideMode)
            {
                attribute.ErrorMessage = Translator.Translate(languageKey);
            }

            if (string.IsNullOrEmpty(attribute.ErrorMessage))
            {
                attribute.ErrorMessage = string.Format("[[{0}]]", languageKey);
            }
        }
示例#30
0
    public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
    {
        if (attribute == null)
        {
            throw new ArgumentNullException(nameof(attribute));
        }
        var type = attribute.GetType();

        if (type == typeof(CustomRequiredAttribute))
        {
            return(new RequiredAttributeAdapter((RequiredAttribute)attribute, stringLocalizer));
        }
        return(innerProvider.GetAttributeAdapter(attribute, stringLocalizer));
    }
        /// <summary>
        /// Creates an <see cref="IAttributeAdapter" /> for the given attribute.
        /// </summary>
        /// <param name="attribute">The attribute to create an adapter for.</param>
        /// <param name="stringLocalizer">The localizer to provide to the adapter.</param>
        /// <returns>An <see cref="IAttributeAdapter" /> for the given attribute.</returns>
        public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
        {
            Requires.NotNull(attribute, nameof(attribute));

            AdapterFactory factory;

            if (factories.TryGetValue(attribute.GetType(), out factory))
            {
                return(factory(attribute, stringLocalizer));
            }

            var inner = new ValidationAttributeAdapterProvider();

            return(inner.GetAttributeAdapter(attribute, stringLocalizer));
        }