// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
///
/// An implementation of which providers validators
/// for attributes which derive from . It also provides
/// a validator for types which implement . To support
/// client side validation, you can either register adapters through the static methods
/// on this class, or by having your validation attributes implement
/// . The logic to support IClientValidatable
/// is implemented in .
///
public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProvider
{
// A factory for validators based on ValidationAttribute.
internal delegate IModelValidator DataAnnotationsModelValidationFactory(ValidationAttribute attribute);
// A factory for validators based on IValidatableObject
private delegate IModelValidator DataAnnotationsValidatableObjectAdapterFactory();
private static bool _addImplicitRequiredAttributeForValueTypes = true;
private readonly Dictionary _attributeFactories =
BuildAttributeFactoriesDictionary();
// Factories for validation attributes
private static readonly DataAnnotationsModelValidationFactory _defaultAttributeFactory =
(attribute) => new DataAnnotationsModelValidator(attribute);
// Factories for IValidatableObject models
private static readonly DataAnnotationsValidatableObjectAdapterFactory _defaultValidatableFactory =
() => new ValidatableObjectAdapter();
internal Dictionary AttributeFactories
{
get { return _attributeFactories; }
}
private static bool AddImplicitRequiredAttributeForValueTypes
{
get { return _addImplicitRequiredAttributeForValueTypes; }
set { _addImplicitRequiredAttributeForValueTypes = value; }
}
protected override IEnumerable GetValidators(ModelMetadata metadata, IEnumerable attributes)
{
var results = new List();
// Produce a validator for each validation attribute we find
foreach (var attribute in attributes.OfType())
{
DataAnnotationsModelValidationFactory factory;
if (!_attributeFactories.TryGetValue(attribute.GetType(), out factory))
{
factory = _defaultAttributeFactory;
}
results.Add(factory(attribute));
}
// Produce a validator if the type supports IValidatableObject
if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType))
{
results.Add(_defaultValidatableFactory());
}
return results;
}
private static Dictionary BuildAttributeFactoriesDictionary()
{
var dict = new Dictionary();
AddValidationAttributeAdapter(dict, typeof(RegularExpressionAttribute),
(attribute) => new RegularExpressionAttributeAdapter((RegularExpressionAttribute)attribute));
AddValidationAttributeAdapter(dict, typeof(MaxLengthAttribute),
(attribute) => new MaxLengthAttributeAdapter((MaxLengthAttribute)attribute));
AddValidationAttributeAdapter(dict, typeof(MinLengthAttribute),
(attribute) => new MinLengthAttributeAdapter((MinLengthAttribute)attribute));
AddValidationAttributeAdapter(dict, typeof(CompareAttribute),
(attribute) => new CompareAttributeAdapter((CompareAttribute)attribute));
AddDataTypeAttributeAdapter(dict, typeof(UrlAttribute), "url");
return dict;
}
private static void AddValidationAttributeAdapter(Dictionary dictionary,
Type validationAttributeType,
DataAnnotationsModelValidationFactory factory)
{
if (validationAttributeType != null)
{
dictionary.Add(validationAttributeType, factory);
}
}
private static void AddDataTypeAttributeAdapter(Dictionary dictionary,
Type attributeType,
string ruleName)
{
AddValidationAttributeAdapter(
dictionary,
attributeType,
(attribute) => new DataTypeAttributeAdapter((DataTypeAttribute)attribute, ruleName));
}
}
}