Updates to the extensibility for validator providers

This change removes reflection from validator providers, and instead
relies on cached metadata in in the modelmetadata.

In general this means that our MVPs don't need to cache anything, they
just look at the metadata and create what they need.

In the case of data-annotations, we update the model details provider to
add validation attributes to the modelmetadata. This would allow someone
to replace the DataAnnotationsValidatorProvider, but still use the
metadata in these attributes.

The change to the IModelValidatorProvider api (to use a context) is
intended to minimize allocations. Currently each validator provider needs
to return a list so you end up with N+1 lists (N validators + a final list
to compine them all). This change will let us just create the final list
(and a small context object). This is a very very high traffic API so it
seemed worth doing.

There's also some general massaging of namespaces and file locations.
This commit is contained in:
Ryan Nowak 2015-03-17 16:47:47 -07:00
parent 1ba4700339
commit f5e7a69693
120 changed files with 919 additions and 569 deletions

View File

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.Internal;

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.DependencyInjection;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Core

View File

@ -1,11 +1,10 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.DependencyInjection;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Core

View File

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Mvc

View File

@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Mvc.OptionDescriptors

View File

@ -8,7 +8,7 @@ using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.DependencyInjection;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Core

View File

@ -3,6 +3,7 @@
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
namespace Microsoft.AspNet.Mvc
{

View File

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc

View File

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc

View File

@ -1,8 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
namespace Microsoft.AspNet.Mvc.Internal
{

View File

@ -6,6 +6,9 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding

View File

@ -84,7 +84,7 @@ namespace Microsoft.AspNet.Mvc
/// <summary>
/// Gets a list of <see cref="ExcludeValidationDescriptor"/> which are used to construct a list
/// of exclude filters by <see cref="IValidationExcludeFiltersProvider"/>.
/// of exclude filters by <see cref="ModelBinding.Validation.IValidationExcludeFiltersProvider"/>.
/// </summary>
public IList<ExcludeValidationDescriptor> ValidationExcludeFilters { get; }
@ -115,7 +115,7 @@ namespace Microsoft.AspNet.Mvc
/// <summary>
/// Gets a list of the <see cref="ModelValidatorProviderDescriptor" />s used by
/// <see cref="ModelBinding.CompositeModelValidatorProvider"/>.
/// <see cref="ModelBinding.Validation.CompositeModelValidatorProvider"/>.
/// </summary>
public IList<ModelValidatorProviderDescriptor> ModelValidatorProviders { get; }

View File

@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Mvc.OptionDescriptors

View File

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.OptionDescriptors

View File

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.OptionDescriptors

View File

@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.OptionDescriptors;
using Microsoft.Framework.Internal;
@ -57,7 +57,7 @@ namespace Microsoft.AspNet.Mvc
/// Adds an <see cref="IModelValidatorProvider"/> to <paramref name="descriptors"/>.
/// </summary>
/// <param name="descriptors">A list of <see cref="ModelValidatorProviderDescriptor"/>.</param>
/// <param name="modelValidatorProvider">An <see cref="IModelBinder"/> instance.</param>
/// <param name="modelValidatorProvider">An <see cref="IModelValidatorProvider"/> instance.</param>
/// <returns>A <see cref="ModelValidatorProviderDescriptor"/> representing the added instance.</returns>
public static ModelValidatorProviderDescriptor Add(
[NotNull] this IList<ModelValidatorProviderDescriptor> descriptors,
@ -75,7 +75,7 @@ namespace Microsoft.AspNet.Mvc
/// <param name="descriptors">A list of <see cref="ModelValidatorProviderDescriptor"/>.</param>
/// <param name="index">The zero-based index at which <paramref name="modelValidatorProvider"/>
/// should be inserted.</param>
/// <param name="modelValidatorProvider">An <see cref="IModelBinder"/> instance.</param>
/// <param name="modelValidatorProvider">An <see cref="IModelValidatorProvider"/> instance.</param>
/// <returns>A <see cref="ModelValidatorProviderDescriptor"/> representing the added instance.</returns>
public static ModelValidatorProviderDescriptor Insert(
[NotNull] this IList<ModelValidatorProviderDescriptor> descriptors,

View File

@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.OptionDescriptors;
namespace Microsoft.AspNet.Mvc

View File

@ -3,6 +3,7 @@
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
namespace Microsoft.AspNet.Mvc
{

View File

@ -12,6 +12,7 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc

View File

@ -8,7 +8,7 @@ using System.Globalization;
using System.Linq;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;

View File

@ -10,8 +10,8 @@ using System.Linq;
using System.Text;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.Rendering.Expressions;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
using Microsoft.Framework.WebEncoders;
@ -746,9 +746,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
modelExplorer.Metadata,
_metadataProvider,
viewContext.HttpContext.RequestServices);
var validatorProviderContext = new ModelValidatorProviderContext(modelExplorer.Metadata);
validatorProvider.GetValidators(validatorProviderContext);
return validatorProvider
.GetValidators(modelExplorer.Metadata)
var validators = validatorProviderContext.Validators;
return
validators
.OfType<IClientModelValidator>()
.SelectMany(v => v.GetClientValidationRules(validationContext));
}

View File

@ -9,6 +9,7 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.Rendering.Expressions;
using Microsoft.Framework.Internal;
using Microsoft.Framework.WebEncoders;

View File

@ -3,6 +3,7 @@
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Rendering

View File

@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
using Microsoft.Framework.WebEncoders;

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Rendering

View File

@ -9,7 +9,7 @@ using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.Framework.DependencyInjection;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
@ -326,10 +326,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
continue;
}
var requiredValidator = bindingContext.OperationBindingContext
.ValidatorProvider
.GetValidators(propertyMetadata)
.FirstOrDefault(v => v != null && v.IsRequired);
var validatorProviderContext = new ModelValidatorProviderContext(propertyMetadata);
bindingContext.OperationBindingContext.ValidatorProvider.GetValidators(validatorProviderContext);
var requiredValidator = validatorProviderContext.Validators
.FirstOrDefault(v => v != null && v.IsRequired);
if (requiredValidator != null)
{
validationInfo.RequiredValidators[propertyName] = requiredValidator;

View File

@ -1,7 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.Framework.Internal;
@ -12,9 +11,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// An implementation of <see cref="IBindingMetadataProvider"/> and <see cref="IDisplayMetadataProvider"/> for
/// the System.ComponentModel.DataAnnotations attribute classes.
/// </summary>
public class DataAnnotationsMetadataDetailsProvider :
public class DataAnnotationsMetadataProvider :
IBindingMetadataProvider,
IDisplayMetadataProvider
IDisplayMetadataProvider,
IValidationMetadataProvider
{
/// <inheritdoc />
public void GetBindingMetadata([NotNull] BindingMetadataProviderContext context)
@ -167,5 +167,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
displayMetadata.TemplateHint = "HiddenInput";
}
}
/// <inheritdoc />
public void GetValidationMetadata([NotNull] ValidationMetadataProviderContext context)
{
foreach (var attribute in context.Attributes.OfType<ValidationAttribute>())
{
context.ValidationMetadata.ValiatorMetadata.Add(attribute);
}
}
}
}

View File

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// An <see cref="IBindingMetadataProvider"/> for <see cref="DataMemberAttribute.IsRequired"/>.
/// </summary>
public class DataMemberRequiredValidationMetadataProvider : IBindingMetadataProvider
{
/// <inheritdoc />
public void GetBindingMetadata([NotNull] BindingMetadataProviderContext context)
{
// Types cannot be required; only properties can
if (context.Key.MetadataKind != ModelMetadataKind.Property)
{
return;
}
var dataMemberAttribute = context
.Attributes
.OfType<DataMemberAttribute>()
.FirstOrDefault();
if (dataMemberAttribute == null || !dataMemberAttribute.IsRequired)
{
return;
}
// isDataContract == true iff the container type has at least one DataContractAttribute
var containerType = context.Key.ContainerType.GetTypeInfo();
var isDataContract = containerType.GetCustomAttribute<DataContractAttribute>() != null;
if (isDataContract)
{
// We don't need to add a validator, just to set IsRequired = true. The validation
// system will do the right thing.
context.BindingMetadata.IsRequired = true;
}
}
}
}

View File

@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
private bool? _isReadOnly;
private bool? _isRequired;
private ModelPropertyCollection _properties;
private ReadOnlyCollection<object> _validatorMetadata;
/// <summary>
/// Creates a new <see cref="DefaultModelMetadata"/>.
@ -356,5 +357,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
return DisplayMetadata.TemplateHint;
}
}
/// <inheritdoc />
public override IReadOnlyList<object> ValidatorMetadata
{
get
{
if (_validatorMetadata == null)
{
_validatorMetadata = new ReadOnlyCollection<object>(ValidationMetadata.ValiatorMetadata);
}
return _validatorMetadata;
}
}
}
}

View File

@ -88,10 +88,39 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
var propertyHelpers = PropertyHelper.GetProperties(key.ModelType);
var propertyEntries = new DefaultMetadataDetailsCache[propertyHelpers.Length];
var propertyEntries = new List<DefaultMetadataDetailsCache>(propertyHelpers.Length);
for (var i = 0; i < propertyHelpers.Length; i++)
{
var propertyHelper = propertyHelpers[i];
if (propertyHelper.Property.DeclaringType != key.ModelType)
{
// If this property was declared on a base type then look for the definition closest to the
// the model type to see if we should include it.
var ignoreProperty = false;
// Walk up the hierarchy until we find the type that actally declares this
// PropertyInfo.
var currentType = key.ModelType.GetTypeInfo();
while (currentType != propertyHelper.Property.DeclaringType.GetTypeInfo())
{
// We've found a 'more proximal' public definition
var declaredProperty = currentType.GetDeclaredProperty(propertyHelper.Name);
if (declaredProperty != null)
{
ignoreProperty = true;
break;
}
currentType = currentType.BaseType.GetTypeInfo();
}
if (ignoreProperty)
{
// There's a better definition, ignore this.
continue;
}
}
var propertyKey = ModelMetadataIdentity.ForProperty(
propertyHelper.Property.PropertyType,
propertyHelper.Name,
@ -101,19 +130,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
key.ModelType,
propertyHelper.Property));
propertyEntries[i] = new DefaultMetadataDetailsCache(propertyKey, attributes);
var propertyEntry = new DefaultMetadataDetailsCache(propertyKey, attributes);
if (propertyHelper.Property.CanRead && propertyHelper.Property.GetMethod?.IsPrivate == true)
{
propertyEntries[i].PropertyAccessor = PropertyHelper.MakeFastPropertyGetter(propertyHelper.Property);
propertyEntry.PropertyAccessor = PropertyHelper.MakeFastPropertyGetter(propertyHelper.Property);
}
if (propertyHelper.Property.CanWrite && propertyHelper.Property.SetMethod?.IsPrivate == true)
{
propertyEntries[i].PropertySetter = PropertyHelper.MakeFastPropertySetter(propertyHelper.Property);
propertyEntry.PropertySetter = PropertyHelper.MakeFastPropertySetter(propertyHelper.Property);
}
propertyEntries.Add(propertyEntry);
}
return propertyEntries;
return propertyEntries.ToArray();
}
/// <summary>

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A default implementation of <see cref="IValidationMetadataProvider"/>.
/// </summary>
public class DefaultValidationMetadataProvider : IValidationMetadataProvider
{
/// <inheritdoc />
public void GetValidationMetadata([NotNull] ValidationMetadataProviderContext context)
{
foreach (var attribute in context.Attributes.OfType<IModelValidator>())
{
context.ValidationMetadata.ValiatorMetadata.Add(attribute);
}
}
}
}

View File

@ -6,7 +6,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// <summary>
/// Marker interface for a provider of metadata details about model objects. Implementations should
/// implement one or more of <see cref="IBindingMetadataProvider"/>, <see cref="IDisplayMetadataProvider"/>,
/// and <see cref="IModelValidatorProvider"/>.
/// and <see cref="IValidationMetadataProvider"/>.
/// </summary>
public interface IMetadataDetailsProvider
{

View File

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
@ -8,5 +10,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// </summary>
public class ValidationMetadata
{
/// <summary>
/// Gets a list of metadata items for validators.
/// </summary>
/// <remarks>
/// <see cref="IValidationMetadataProvider"/> implementations should store metadata items
/// in this list, to be consumed later by an <see cref="Validation.IModelValidatorProvider"/>.
/// </remarks>
public IList<object> ValiatorMetadata { get; } = new List<object>();
}
}

View File

@ -199,6 +199,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary>
public abstract string TemplateHint { get; }
/// <summary>
/// Gets a collection of metadata items for validators.
/// </summary>
public abstract IReadOnlyList<object> ValidatorMetadata { get;}
/// <summary>
/// Gets a value indicating whether <see cref="ModelType"/> is a simple type.
/// </summary>

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
namespace Microsoft.AspNet.Mvc.ModelBinding
{

View File

@ -1,54 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public abstract class AssociatedValidatorProvider : IModelValidatorProvider
{
public IEnumerable<IModelValidator> GetValidators([NotNull] ModelMetadata metadata)
{
if (metadata.ContainerType != null && !string.IsNullOrEmpty(metadata.PropertyName))
{
return GetValidatorsForProperty(metadata);
}
return GetValidatorsForType(metadata);
}
protected abstract IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata,
IEnumerable<object> attributes);
private IEnumerable<IModelValidator> GetValidatorsForProperty(ModelMetadata metadata)
{
var propertyName = metadata.PropertyName;
var bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;
var property = metadata.ContainerType
.GetProperty(propertyName, bindingFlags);
if (property == null)
{
throw new ArgumentException(
Resources.FormatCommon_PropertyNotFound(
metadata.ContainerType.FullName,
metadata.PropertyName),
"metadata");
}
var attributes = ModelAttributes.GetAttributesForProperty(metadata.ContainerType, property);
return GetValidators(metadata, attributes);
}
private IEnumerable<IModelValidator> GetValidatorsForType(ModelMetadata metadata)
{
var attributes = ModelAttributes.GetAttributesForType(metadata.ModelType);
return GetValidators(metadata, attributes);
}
}
}

View File

@ -4,7 +4,7 @@
using System;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ClientModelValidationContext
{

View File

@ -6,7 +6,7 @@ using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class CompareAttributeAdapter : DataAnnotationsModelValidator<CompareAttribute>
{

View File

@ -2,10 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Default implementation for <see cref="ICompositeModelValidatorProvider"/>.
@ -25,9 +24,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public IReadOnlyList<IModelValidatorProvider> ValidatorProviders { get; }
public IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata)
public void GetValidators(ModelValidatorProviderContext context)
{
return ValidatorProviders.SelectMany(v => v.GetValidators(metadata));
foreach (var validatorProvider in ValidatorProviders)
{
validatorProvider.GetValidators(context);
}
}
}
}

View File

@ -7,7 +7,7 @@ using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class DataAnnotationsModelValidator : IModelValidator, IClientModelValidator
{

View File

@ -3,7 +3,7 @@
using System.ComponentModel.DataAnnotations;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class DataAnnotationsModelValidator<TAttribute> : DataAnnotationsModelValidator
where TAttribute : ValidationAttribute

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// An implementation of <see cref="IModelValidatorProvider"/> which providers validators
@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <see cref="IClientModelValidator"/>. The logic to support <see cref="IClientModelValidator"/>
/// is implemented in <see cref="DataAnnotationsModelValidator"/>.
/// </summary>
public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProvider
public class DataAnnotationsModelValidatorProvider : IModelValidatorProvider
{
// A factory for validators based on ValidationAttribute.
internal delegate IModelValidator DataAnnotationsModelValidationFactory(ValidationAttribute attribute);
@ -30,7 +30,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private static readonly DataAnnotationsValidatableObjectAdapterFactory _defaultValidatableFactory =
() => new ValidatableObjectAdapter();
private static bool _addImplicitRequiredAttributeForValueTypes = true;
private readonly Dictionary<Type, DataAnnotationsModelValidationFactory> _attributeFactories =
BuildAttributeFactoriesDictionary();
@ -42,35 +41,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
get { return _attributeFactories; }
}
private static bool AddImplicitRequiredAttributeForValueTypes
public void GetValidators(ModelValidatorProviderContext context)
{
get { return _addImplicitRequiredAttributeForValueTypes; }
set { _addImplicitRequiredAttributeForValueTypes = value; }
}
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata,
IEnumerable<object> attributes)
{
var results = new List<IModelValidator>();
// Produce a validator for each validation attribute we find
foreach (var attribute in attributes.OfType<ValidationAttribute>())
foreach (var attribute in context.ValidatorMetadata.OfType<ValidationAttribute>())
{
DataAnnotationsModelValidationFactory factory;
if (!_attributeFactories.TryGetValue(attribute.GetType(), out factory))
{
factory = _defaultAttributeFactory;
}
results.Add(factory(attribute));
context.Validators.Add(factory(attribute));
}
// Produce a validator if the type supports IValidatableObject
if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType))
if (typeof(IValidatableObject).IsAssignableFrom(context.ModelMetadata.ModelType))
{
results.Add(_defaultValidatableFactory());
context.Validators.Add(_defaultValidatableFactory());
}
return results;
}
private static Dictionary<Type, DataAnnotationsModelValidationFactory> BuildAttributeFactoriesDictionary()

View File

@ -1,54 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
/// <summary>
/// This <see cref="IModelValidatorProvider"/> provides a required ModelValidator for members marked
/// as <c>[DataMember(IsRequired=true)]</c>.
/// </summary>
public class DataMemberModelValidatorProvider : AssociatedValidatorProvider
{
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata,
IEnumerable<object> attributes)
{
// Types cannot be required; only properties can
if (metadata.ContainerType == null || string.IsNullOrEmpty(metadata.PropertyName))
{
return Enumerable.Empty<IModelValidator>();
}
if (IsRequiredDataMember(metadata.ContainerType, attributes))
{
return new[] { new RequiredMemberModelValidator() };
}
return Enumerable.Empty<IModelValidator>();
}
internal static bool IsRequiredDataMember(Type containerType, IEnumerable<object> attributes)
{
var dataMemberAttribute = attributes.OfType<DataMemberAttribute>()
.FirstOrDefault();
if (dataMemberAttribute != null)
{
// isDataContract == true iff the container type has at least one DataContractAttribute
var isDataContract = containerType.GetTypeInfo()
.GetCustomAttributes()
.OfType<DataContractAttribute>()
.Any();
if (isDataContract && dataMemberAttribute.IsRequired)
{
return true;
}
}
return false;
}
}
}

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// A validation adapter that is used to map <see cref="DataTypeAttribute"/>'s to a single client side validation

View File

@ -0,0 +1,28 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// A default <see cref="IModelValidatorProvider"/>.
/// </summary>
/// <remarks>
/// The <see cref="DefaultModelValidatorProvider"/> provides validators from <see cref="IModelValidator"/>
/// instances in <see cref="ModelMetadata.ValidatorMetadata"/>.
/// </remarks>
public class DefaultModelValidatorProvider : IModelValidatorProvider
{
/// <inheritdoc />
public void GetValidators(ModelValidatorProviderContext context)
{
foreach (var metadata in context.ValidatorMetadata)
{
var validator = metadata as IModelValidator;
if (validator != null)
{
context.Validators.Add(validator);
}
}
}
}
}

View File

@ -10,7 +10,7 @@ using System.Runtime.CompilerServices;
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Recursively validate an object.
@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
string modelKey,
ModelExplorer modelExplorer,
ValidationContext validationContext,
IEnumerable<IModelValidator> validators)
IList<IModelValidator> validators)
{
// Recursion guard to avoid stack overflows
RuntimeHelpers.EnsureSufficientExecutionStack();
@ -84,7 +84,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// the same for all the elements of the array, we do not do GetValidators for each element,
// instead we just pass them over. See ValidateElements function.
var validatorProvider = validationContext.ModelValidationContext.ValidatorProvider;
validators = validatorProvider.GetValidators(modelExplorer.Metadata);
var validatorProviderContext = new ModelValidatorProviderContext(modelExplorer.Metadata);
validatorProvider.GetValidators(validatorProviderContext);
validators = validatorProviderContext.Validators;
}
// We don't need to recursively traverse the graph for null values
@ -186,7 +189,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var elementType = GetElementType(model.GetType());
var elementMetadata = _modelMetadataProvider.GetMetadataForType(elementType);
var validators = validationContext.ModelValidationContext.ValidatorProvider.GetValidators(elementMetadata);
var validatorProvider = validationContext.ModelValidationContext.ValidatorProvider;
var validatorProviderContext = new ModelValidatorProviderContext(elementMetadata);
validatorProvider.GetValidators(validatorProviderContext);
var validators = validatorProviderContext.Validators;
// If there are no validators or the object is null we bail out quickly
// when there are large arrays of null, this will save a significant amount of processing
@ -220,20 +227,28 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
string modelKey,
ModelExplorer modelExplorer,
ValidationContext validationContext,
IEnumerable<IModelValidator> validators)
IList<IModelValidator> validators)
{
var isValid = true;
var modelState = validationContext.ModelValidationContext.ModelState;
var fieldValidationState = modelState.GetFieldValidationState(modelKey);
if (fieldValidationState == ModelValidationState.Invalid)
{
// Even if we have no validators it's possible that model binding may have added a
// validation error (conversion error, missing data). We want to still run
// validators even if that's the case.
isValid = false;
}
// When the are no validators we bail quickly. This saves a GetEnumerator allocation.
// In a large array (tens of thousands or more) scenario it's very significant.
var validatorsAsCollection = validators as ICollection;
if (validatorsAsCollection == null || validatorsAsCollection.Count > 0)
if (validators == null || validators.Count > 0)
{
var modelValidationContext =
new ModelValidationContext(validationContext.ModelValidationContext, modelExplorer);
var modelState = validationContext.ModelValidationContext.ModelState;
var modelValidationState = modelState.GetValidationState(modelKey);
var fieldValidationState = modelState.GetFieldValidationState(modelKey);
// If either the model or its properties are unvalidated, validate them now.
if (modelValidationState == ModelValidationState.Unvalidated ||
@ -257,10 +272,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
}
else if (fieldValidationState == ModelValidationState.Invalid)
{
isValid = false;
}
}
if (isValid)

View File

@ -3,7 +3,7 @@
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public interface IClientModelValidator
{

View File

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Represents aggregate of <see cref="IModelValidatorProvider"/>s that delegates to its underlying providers.

View File

@ -4,7 +4,7 @@
using System;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Provides an interface which is used to determine if <see cref="Type"/>s are excluded from model validation.

View File

@ -3,12 +3,26 @@
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Validates a model value.
/// </summary>
public interface IModelValidator
{
/// <summary>
/// Gets a value indicating whether or not this validator validates that a required value
/// has been provided for the model.
/// </summary>
bool IsRequired { get; }
/// <summary>
/// Validates the model value.
/// </summary>
/// <param name="context">The <see cref="ModelValidationContext"/>.</param>
/// <returns>
/// A list of <see cref="ModelValidationResult"/> indicating the results of validating the model value.
/// </returns>
IEnumerable<ModelValidationResult> Validate(ModelValidationContext context);
}
}

View File

@ -1,12 +1,21 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Provides validators for a model value.
/// </summary>
public interface IModelValidatorProvider
{
IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata);
/// <summary>
/// Gets the validators for <see cref="ModelValidatorProviderContext.ModelMetadata"/>.
/// </summary>
/// <param name="context">The <see cref="ModelValidatorProviderContext"/>.</param>
/// <remarks>
/// Implementations should add <see cref="IModelValidator"/> instances to
/// <see cref="ModelValidatorProviderContext.Validators"/>.
/// </remarks>
void GetValidators(ModelValidatorProviderContext context);
}
}

View File

@ -3,7 +3,7 @@
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Provides an activated collection of <see cref="IModelValidatorProvider"/> instances.

View File

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Provides methods to validate an object graph.

View File

@ -2,9 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Provides an activated collection of <see cref="IExcludeTypeValidationFilter"/> instances.

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class MaxLengthAttributeAdapter : DataAnnotationsModelValidator<MaxLengthAttribute>
{

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class MinLengthAttributeAdapter : DataAnnotationsModelValidator<MinLengthAttribute>
{

View File

@ -3,7 +3,7 @@
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Represents client side validation rule that determines if two values are equal.

View File

@ -3,7 +3,7 @@
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ModelClientValidationMaxLengthRule : ModelClientValidationRule
{

View File

@ -3,7 +3,7 @@
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ModelClientValidationMinLengthRule : ModelClientValidationRule
{

View File

@ -3,7 +3,7 @@
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ModelClientValidationRangeRule : ModelClientValidationRule
{

View File

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ModelClientValidationRegexRule : ModelClientValidationRule
{

View File

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ModelClientValidationRequiredRule : ModelClientValidationRule
{

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ModelClientValidationRule
{

View File

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ModelClientValidationStringLengthRule : ModelClientValidationRule
{

View File

@ -0,0 +1,49 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// A context for <see cref="IModelValidatorProvider"/>.
/// </summary>
public class ModelValidatorProviderContext
{
/// <summary>
/// Creates a new <see cref="ModelValidatorProviderContext"/>.
/// </summary>
/// <param name="modelMetadata">The <see cref="ModelBinding.ModelMetadata"/>.</param>
public ModelValidatorProviderContext(ModelMetadata modelMetadata)
{
ModelMetadata = modelMetadata;
}
/// <summary>
/// Gets the <see cref="ModelBinding.ModelMetadata"/>.
/// </summary>
public ModelMetadata ModelMetadata { get; }
/// <summary>
/// Gets the validator metadata.
/// </summary>
/// <remarks>
/// This property provides convenience access to <see cref="ModelMetadata.ValidatorMetadata"/>.
/// </remarks>
public IReadOnlyList<object> ValidatorMetadata
{
get
{
return ModelMetadata.ValidatorMetadata;
}
}
/// <summary>
/// Gets the list of <see cref="IModelValidator"/> instances. <see cref="IModelValidatorProvider"/> instances
/// should add validators to this list when
/// <see cref="IModelValidatorProvider.GetValidators(ModelValidatorProviderContext)"/>
/// is called.
/// </summary>
public IList<IModelValidator> Validators { get; } = new List<IModelValidator>();
}
}

View File

@ -1,10 +1,9 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ModelValidationContext
{

View File

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ModelValidationResult
{

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class RangeAttributeAdapter : DataAnnotationsModelValidator<RangeAttribute>
{

View File

@ -4,7 +4,7 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
internal class ReferenceEqualityComparer : IEqualityComparer<object>
{

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class RegularExpressionAttributeAdapter : DataAnnotationsModelValidator<RegularExpressionAttribute>
{

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class RequiredAttributeAdapter : DataAnnotationsModelValidator<RequiredAttribute>
{

View File

@ -1,21 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class RequiredMemberModelValidator : IModelValidator
{
public bool IsRequired
{
get { return true; }
}
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
return Enumerable.Empty<ModelValidationResult>();
}
}
}

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.Reflection;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Identifies the simple types that the default model binding validation will exclude.

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class StringLengthAttributeAdapter : DataAnnotationsModelValidator<StringLengthAttribute>
{

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ValidatableObjectAdapter : IModelValidator
{

View File

@ -8,8 +8,8 @@ using System.Text;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.WebApiCompatShim;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
using Newtonsoft.Json;

View File

@ -5,6 +5,7 @@ using System;
using System.Xml.Linq;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.Framework.OptionsModel;
using Microsoft.Net.Http.Headers;
@ -62,11 +63,13 @@ namespace Microsoft.AspNet.Mvc
// Set up metadata providers
options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
options.ModelMetadataDetailsProviders.Add(new DataAnnotationsMetadataDetailsProvider());
options.ModelMetadataDetailsProviders.Add(new DefaultValidationMetadataProvider());
options.ModelMetadataDetailsProviders.Add(new DataAnnotationsMetadataProvider());
options.ModelMetadataDetailsProviders.Add(new DataMemberRequiredValidationMetadataProvider());
// Set up validators
options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider());
options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider());
options.ModelValidatorProviders.Add(new DataMemberModelValidatorProvider());
// Add types to be excluded from Validation
options.ValidationExcludeFilters.Add(new SimpleTypesExcludeFilter());

View File

@ -9,6 +9,7 @@ using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.OptionDescriptors;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.AspNet.Mvc.Razor.Directives;

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Text;
@ -10,8 +9,8 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
@ -13,9 +12,9 @@ using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.OptionsModel;
using Moq;
using Xunit;

View File

@ -12,6 +12,7 @@ using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing;
using Microsoft.AspNet.WebUtilities;
@ -1580,8 +1581,8 @@ namespace Microsoft.AspNet.Mvc.Test
.Returns(validationResult);
var provider = new Mock<IModelValidatorProvider>();
provider.Setup(v => v.GetValidators(It.IsAny<ModelMetadata>()))
.Returns(new[] { validator1.Object });
provider.Setup(v => v.GetValidators(It.IsAny<ModelValidatorProviderContext>()))
.Callback<ModelValidatorProviderContext>(c => c.Validators.Add(validator1.Object));
var binder = new Mock<IModelBinder>();
var controller = GetController(binder.Object, provider: null);
@ -1613,8 +1614,8 @@ namespace Microsoft.AspNet.Mvc.Test
.Returns(validationResult);
var provider = new Mock<IModelValidatorProvider>();
provider.Setup(v => v.GetValidators(It.IsAny<ModelMetadata>()))
.Returns(new[] { validator1.Object });
provider.Setup(v => v.GetValidators(It.IsAny<ModelValidatorProviderContext>()))
.Callback<ModelValidatorProviderContext>(c => c.Validators.Add(validator1.Object));
var binder = new Mock<IModelBinder>();
var controller = GetController(binder.Object, provider: null);

View File

@ -6,6 +6,7 @@ using System.Reflection;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Moq;

View File

@ -3,9 +3,9 @@
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class MockModelValidatorProviderProvider: IModelValidatorProviderProvider
public class MockModelValidatorProviderProvider : IModelValidatorProviderProvider
{
public List<IModelValidatorProvider> ModelValidatorProviders { get; } = new List<IModelValidatorProvider>();

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.OptionsModel;
using Moq;
using Xunit;
@ -52,9 +53,8 @@ namespace Microsoft.AspNet.Mvc.OptionDescriptors
public ITestService Service { get; private set; }
public IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata)
public void GetValidators(ModelValidatorProviderContext context)
{
throw new NotImplementedException();
}
}

View File

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Testing;
using Microsoft.Framework.Internal;
using Xunit;
@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Mvc.OptionDescriptors
{
// Arrange
var expected = "The type 'System.String' must derive from " +
"'Microsoft.AspNet.Mvc.ModelBinding.IExcludeTypeValidationFilter'.";
"'Microsoft.AspNet.Mvc.ModelBinding.Validation.IExcludeTypeValidationFilter'.";
var type = typeof(string);
// Act & Assert

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Testing;
using Xunit;
@ -54,9 +55,8 @@ namespace Microsoft.AspNet.Mvc.OptionDescriptors
private class TestModelValidatorProvider : IModelValidatorProvider
{
public IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata)
public void GetValidators(ModelValidatorProviderContext context)
{
throw new NotImplementedException();
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.OptionDescriptors;
using Moq;
using Xunit;
@ -52,7 +53,7 @@ namespace Microsoft.AspNet.Mvc
// Arrange
var provider1 = Mock.Of<IModelValidatorProvider>();
var provider2 = Mock.Of<IModelValidatorProvider>();
var type1 = typeof(DataMemberModelValidatorProvider);
var type1 = typeof(DefaultModelValidatorProvider);
var type2 = typeof(DataAnnotationsModelValidatorProvider);
var collection = new List<ModelValidatorProviderDescriptor>();
@ -66,7 +67,7 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(4, collection.Count);
Assert.Same(provider1, collection[0].Instance);
Assert.Same(provider2, collection[1].Instance);
Assert.IsType<DataMemberModelValidatorProvider>(collection[2].OptionType);
Assert.IsType<DefaultModelValidatorProvider>(collection[2].OptionType);
Assert.IsType<DataAnnotationsModelValidatorProvider>(collection[3].OptionType);
}
}

View File

@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Routing;
using Moq;
using Xunit;
@ -416,4 +417,4 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public int IncludedExplicitly2 { get; set; }
}
}
}
}

View File

@ -3,20 +3,18 @@
#if DNX451
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
namespace Microsoft.AspNet.Mvc.Test
{
public class ModelBindingHelperTest
{

View File

@ -2,10 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;

View File

@ -9,6 +9,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Testing;
using Microsoft.Framework.Internal;

View File

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Microsoft.AspNet.DataProtection;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.WebEncoders;

View File

@ -6,9 +6,10 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.DependencyInjection;
using Moq;
using Xunit;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
{

View File

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Moq;
using Xunit;
@ -461,8 +462,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
private static IModelValidatorProvider GetValidatorProvider(params IModelValidator[] validators)
{
var provider = new Mock<IModelValidatorProvider>();
provider.Setup(v => v.GetValidators(It.IsAny<ModelMetadata>()))
.Returns(validators ?? Enumerable.Empty<IModelValidator>());
provider
.Setup(v => v.GetValidators(It.IsAny<ModelValidatorProviderContext>()))
.Callback<ModelValidatorProviderContext>(c =>
{
if (validators == null)
{
return;
}
foreach (var validator in validators)
{
c.Validators.Add(validator);
}
});
return provider.Object;
}

View File

@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Moq;
using Xunit;

View File

@ -9,6 +9,7 @@ using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Testing;
using Moq;
using Xunit;
@ -1194,10 +1195,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
isModelSet: false,
key: "foo");
var requiredValidator = bindingContext.OperationBindingContext
.ValidatorProvider
.GetValidators(propertyMetadata)
.FirstOrDefault(v => v.IsRequired);
var validatorProvider = bindingContext.OperationBindingContext.ValidatorProvider;
var validatorProviderContext = new ModelValidatorProviderContext(propertyMetadata);
validatorProvider.GetValidators(validatorProviderContext);
var requiredValidator = validatorProviderContext.Validators.FirstOrDefault(v => v.IsRequired);
var testableBinder = new TestableMutableObjectModelBinder();
@ -1327,11 +1329,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
key: "foo",
isModelSet: true);
var validatorProvider = bindingContext.OperationBindingContext.ValidatorProvider;
var validatorProviderContext = new ModelValidatorProviderContext(propertyMetadata);
validatorProvider.GetValidators(validatorProviderContext);
var requiredValidator = bindingContext.OperationBindingContext
.ValidatorProvider
.GetValidators(propertyMetadata)
.FirstOrDefault(v => v.IsRequired);
var requiredValidator = validatorProviderContext.Validators.FirstOrDefault(v => v.IsRequired);
var testableBinder = new TestableMutableObjectModelBinder();
@ -1526,12 +1528,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private static ModelBindingContext CreateContext(ModelMetadata metadata, object model)
{
var providers = new IModelValidatorProvider[]
{
new DataAnnotationsModelValidatorProvider(),
new DataMemberModelValidatorProvider()
};
return new ModelBindingContext
{
Model = model,
@ -1541,17 +1537,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
OperationBindingContext = new OperationBindingContext
{
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
ValidatorProvider = new CompositeModelValidatorProvider(providers)
ValidatorProvider = TestModelValidatorProvider.CreateDefaultProvider(),
}
};
}
private static IModelValidator GetRequiredValidator(ModelBindingContext bindingContext, ModelMetadata propertyMetadata)
{
return bindingContext.OperationBindingContext
.ValidatorProvider
.GetValidators(propertyMetadata)
.FirstOrDefault(v => v.IsRequired);
var validatorProvider = bindingContext.OperationBindingContext.ValidatorProvider;
var validatorProviderContext = new ModelValidatorProviderContext(propertyMetadata);
validatorProvider.GetValidators(validatorProviderContext);
return validatorProviderContext.Validators.FirstOrDefault(v => v.IsRequired);
}
private static ModelMetadata GetMetadataForCanUpdateProperty(string propertyName)

View File

@ -7,7 +7,7 @@ using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
public class DataAnnotationsMetadataDetailsProviderTest
public class DataAnnotationsMetadataProviderTest
{
// Includes attributes with a 'simple' effect on display details.
public static TheoryData<object, Func<DisplayMetadata, object>, object> DisplayDetailsData
@ -52,7 +52,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
object expected)
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var provider = new DataAnnotationsMetadataProvider();
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new object[] { attribute });
@ -69,7 +69,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
public void GetDisplayDetails_FindsDisplayFormat_FromDataType()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var provider = new DataAnnotationsMetadataProvider();
var dataType = new DataTypeAttribute(DataType.Currency);
var displayFormat = dataType.DisplayFormat; // Non-null for DataType.Currency.
@ -89,7 +89,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
public void GetDisplayDetails_FindsDisplayFormat_OverridingDataType()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var provider = new DataAnnotationsMetadataProvider();
var dataType = new DataTypeAttribute(DataType.Time); // Has a non-null DisplayFormat.
var displayFormat = new DisplayFormatAttribute() // But these values override the values from DataType
@ -112,7 +112,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
public void GetDisplayDetails_EditableAttribute_SetsReadOnly()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var provider = new DataAnnotationsMetadataProvider();
var editable = new EditableAttribute(allowEdit: false);
@ -131,7 +131,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
public void GetDisplayDetails_RequiredAttribute_SetsRequired()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var provider = new DataAnnotationsMetadataProvider();
var required = new RequiredAttribute();
@ -151,7 +151,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
public void GetDisplayDetails_DisplayAttribute_NameFromResources()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var provider = new DataAnnotationsMetadataProvider();
var display = new DisplayAttribute()
{
@ -175,7 +175,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
public void GetDisplayDetails_DisplayAttribute_DescriptionFromResources()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var provider = new DataAnnotationsMetadataProvider();
var display = new DisplayAttribute()
{

View File

@ -0,0 +1,96 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.Serialization;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
public class DataMemberBindingMetadataProviderTest
{
[Fact]
public void ClassWithoutAttributes_NotRequired()
{
// Arrange
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
// Act
var metadata = metadataProvider.GetMetadataForProperty(
typeof(ClassWithoutAttributes),
"TheProperty");
// Assert
Assert.False(metadata.IsRequired);
}
private class ClassWithoutAttributes
{
public string TheProperty { get; set; }
}
[Fact]
public void ClassWithDataMemberIsRequiredTrue_Validator()
{
// Arrange
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
// Act
var metadata = metadataProvider.GetMetadataForProperty(
typeof(ClassWithDataMemberIsRequiredTrue),
"TheProperty");
// Assert
Assert.True(metadata.IsRequired);
}
[DataContract]
private class ClassWithDataMemberIsRequiredTrue
{
[DataMember(IsRequired = true)]
public string TheProperty { get; set; }
}
[Fact]
public void ClassWithDataMemberIsRequiredFalse_NoValidator()
{
// Arrange
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
// Act
var metadata = metadataProvider.GetMetadataForProperty(
typeof(ClassWithDataMemberIsRequiredFalse),
"TheProperty");
// Assert
Assert.False(metadata.IsRequired);
}
[DataContract]
private class ClassWithDataMemberIsRequiredFalse
{
[DataMember(IsRequired = false)]
public int TheProperty { get; set; }
}
[Fact]
public void ClassWithDataMemberIsRequiredTrueWithoutDataContract_NoValidator()
{
// Arrange
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
// Act
var metadata = metadataProvider.GetMetadataForProperty(
typeof(ClassWithDataMemberIsRequiredTrueWithoutDataContract),
"TheProperty");
// Assert
Assert.False(metadata.IsRequired);
}
private class ClassWithDataMemberIsRequiredTrueWithoutDataContract
{
[DataMember(IsRequired = true)]
public int TheProperty { get; set; }
}
}
}

View File

@ -110,6 +110,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
Assert.Equal("OnPropertyType", Assert.IsType<ModelAttribute>(attributes[1]).Value);
}
[Fact]
public void GetMetadataForProperties_ExcludesHiddenProperties()
{
// Arrange
var provider = CreateProvider();
// Act
var metadata = provider.GetMetadataForProperties(typeof(DerivedModelWithHiding));
// Assert
var propertyMetadata = Assert.Single(metadata);
Assert.Equal(typeof(string), propertyMetadata.ModelType);
}
private static DefaultModelMetadataProvider CreateProvider()
{
return new DefaultModelMetadataProvider(new EmptyCompositeMetadataDetailsProvider());
@ -149,5 +163,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
private void GetMetadataForParameterTestMethod([Model("OnParameter")] ModelType parameter)
{
}
private class BaseModelWithHiding
{
public int Property { get; set; }
}
private class DerivedModelWithHiding : BaseModelWithHiding
{
public new string Property { get; set; }
}
}
}
}

View File

@ -701,7 +701,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
: base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[]
{
new DefaultBindingMetadataProvider(),
new DataAnnotationsMetadataDetailsProvider(),
new DataAnnotationsMetadataProvider(),
}))
{
_attributes = attributes;

Some files were not shown because too many files have changed in this diff Show More