From f5e7a69693e517666766517cd64e4d4e77e00659 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Tue, 17 Mar 2015 16:47:47 -0700 Subject: [PATCH] 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. --- src/Microsoft.AspNet.Mvc.Core/Controller.cs | 1 + .../ControllerActionInvoker.cs | 2 +- .../ControllerActionInvokerProvider.cs | 3 +- .../DefaultControllerActionArgumentBinder.cs | 1 + ...DefaultValidationExcludeFiltersProvider.cs | 2 +- .../FilterActionInvoker.cs | 2 +- .../Filters/ResourceExecutingContext.cs | 1 + .../DefaultTypeBasedExcludeFilter.cs | 2 +- .../DefaultTypeNameBasedExcludeFilter.cs | 2 +- .../ModelClientValidationRemoteRule.cs | 3 +- .../ModelBinders/BodyModelBinder.cs | 3 + src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs | 4 +- .../DefaultModelValidatorProviderProvider.cs | 2 +- .../ExcludeValidationDescriptor.cs | 2 +- .../ModelValidatorProviderDescriptor.cs | 2 +- ...elValidatorProviderDescriptorExtensions.cs | 6 +- .../ValidationExcludeFiltersExtensions.cs | 2 +- .../ParameterBinding/ActionBindingContext.cs | 1 + .../ParameterBinding/ModelBindingHelper.cs | 1 + .../RemoteAttribute.cs | 2 +- .../Rendering/Html/DefaultHtmlGenerator.cs | 11 +- .../Rendering/Html/HtmlHelper.cs | 1 + .../Rendering/Html/IHtmlGenerator.cs | 1 + .../Rendering/IHtmlHelper.cs | 1 + ...nobtrusiveValidationAttributesGenerator.cs | 2 +- .../Binders/MutableObjectModelBinder.cs | 11 +- ....cs => DataAnnotationsMetadataProvider.cs} | 15 +- .../DataMemberBindingMetadataProvider.cs | 45 +++ .../Metadata/DefaultModelMetadata.cs | 15 + .../Metadata/DefaultModelMetadataProvider.cs | 41 ++- .../DefaultValidationMetadataProvider.cs | 24 ++ .../Metadata/IMetadataDetailsProvider.cs | 2 +- .../Metadata/ValidationMetadata.cs | 10 + .../ModelMetadata.cs | 5 + .../OperationBindingContext.cs | 1 + .../Validation/AssociatedValidatorProvider.cs | 54 ---- .../ClientModelValidationContext.cs | 2 +- .../Validation/CompareAttributeAdapter.cs | 2 +- .../CompositeModelValidatorProvider.cs | 10 +- .../DataAnnotationsModelValidator.cs | 2 +- ...taAnnotationsModelValidatorOfTAttribute.cs | 2 +- .../DataAnnotationsModelValidatorProvider.cs | 28 +- .../DataMemberModelValidatorProvider.cs | 54 ---- .../Validation/DataTypeAttributeAdapter.cs | 2 +- .../DefaultModelValidatorProvider.cs | 28 ++ .../Validation/DefaultObjectValidator.cs | 37 ++- .../Validation/IClientModelValidator.cs | 2 +- .../ICompositeModelValidatorProvider.cs | 2 +- .../IExcludeTypeValidationFilter.cs | 2 +- .../Validation/IModelValidator.cs | 16 +- .../Validation/IModelValidatorProvider.cs | 17 +- .../IModelValidatorProviderProvider.cs | 2 +- .../{ => Validation}/IObjectModelValidator.cs | 2 +- .../IValidationExcludeFiltersProvider.cs | 3 +- .../Validation/MaxLengthAttributeAdapter.cs | 2 +- .../Validation/MinLengthAttributeAdapter.cs | 2 +- .../ModelClientValidationEqualToRule.cs | 2 +- .../ModelClientValidationMaxLengthRule.cs | 2 +- .../ModelClientValidationMinLengthRule.cs | 2 +- .../ModelClientValidationRangeRule.cs | 2 +- .../ModelClientValidationRegexRule.cs | 2 +- .../ModelClientValidationRequiredRule.cs | 2 +- .../Validation/ModelClientValidationRule.cs | 2 +- .../ModelClientValidationStringLengthRule.cs | 2 +- .../ModelValiatorProviderContext.cs | 49 ++++ .../Validation/ModelValidationContext.cs | 3 +- .../Validation/ModelValidationResult.cs | 2 +- .../Validation/RangeAttributeAdapter.cs | 2 +- .../Validation/ReferenceEqualityComparer.cs | 2 +- .../RegularExpressionAttributeAdapter.cs | 2 +- .../Validation/RequiredAttributeAdapter.cs | 2 +- .../RequiredMemberModelValidator.cs | 21 -- .../SimpleTypesExcludeFilter.cs | 2 +- .../StringLengthAttributeAdapter.cs | 2 +- .../Validation/ValidatableObjectAdapter.cs | 2 +- .../ApiController.cs | 2 +- src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs | 7 +- src/Microsoft.AspNet.Mvc/MvcServices.cs | 1 + .../BodyModelBinderTests.cs | 3 +- .../ControllerActionInvokerTest.cs | 3 +- .../ControllerTests.cs | 9 +- .../DefaultControllerFactoryTest.cs | 1 + .../MockModelValidatorProviderProvider.cs | 4 +- .../DefaultValidationProviderProviderTest.cs | 4 +- .../ExcludeValidationDescriptorTests.cs | 4 +- .../ModelValidatorProviderDescriptor.cs | 4 +- ...lidatorProviderDescriptorExtensionsTest.cs | 5 +- .../ControllerActionArgumentBinderTests.cs | 3 +- .../ModelBindingHelperTest.cs | 6 +- .../RemoteAttributeTest.cs | 2 +- .../Rendering/DefaultEditorTemplatesTest.cs | 1 + .../Rendering/DefaultTemplatesUtilities.cs | 1 + ...nderTypeBasedModelBinderModelBinderTest.cs | 3 +- .../Binders/CompositeModelBinderTest.cs | 17 +- .../Binders/KeyValuePairModelBinderTest.cs | 1 + .../Binders/MutableObjectModelBinderTest.cs | 35 ++- ...=> DataAnnotationsMetadataProviderTest.cs} | 16 +- .../DataMemberBindingMetadataProviderTest.cs | 96 +++++++ .../DefaultModelMetadataProviderTest.cs | 26 +- .../Metadata/ModelMetadataProviderTest.cs | 2 +- .../Metadata/ModelMetadataTest.cs | 8 + .../SimpleTypeExcludeFilterTest.cs | 2 +- .../AssociatedValidatorProviderTest.cs | 103 ------- .../Validation/CompareAttributeAdapterTest.cs | 2 +- .../CompositeModelValidatorProviderTest.cs | 28 +- ...taAnnotationsModelValidatorProviderTest.cs | 54 ++-- .../DataAnnotationsModelValidatorTest.cs | 3 +- .../DataMemberModelValidatorProviderTest.cs | 95 ------- .../DefaultModelValidatorProviderTest.cs | 264 ++++++++++++++++++ .../Validation/DefaultObjectValidatorTests.cs | 14 +- .../MaxLengthAttributeAdapterTest.cs | 2 +- .../MinLengthAttributeAdapterTest.cs | 2 +- .../Validation/RangeAttributeAdapterTest.cs | 2 +- .../ReferenceEqualityComparerTest.cs | 2 +- .../RequiredAttributeAdapterTest.cs | 2 +- .../StringLengthAttributeAdapterTest.cs | 2 +- .../MvcOptionsSetupTest.cs | 5 +- .../TestModelMetadataProvider.cs | 8 +- .../TestModelValidatorProvider.cs | 27 ++ .../Controllers/VehicleController.cs | 2 +- 120 files changed, 919 insertions(+), 569 deletions(-) rename src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/{DataAnnotationsMetadataDetailsProvider.cs => DataAnnotationsMetadataProvider.cs} (93%) create mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DataMemberBindingMetadataProvider.cs create mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultValidationMetadataProvider.cs delete mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Validation/AssociatedValidatorProvider.cs delete mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataMemberModelValidatorProvider.cs create mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DefaultModelValidatorProvider.cs rename src/Microsoft.AspNet.Mvc.ModelBinding/{ => Validation}/IObjectModelValidator.cs (92%) create mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValiatorProviderContext.cs delete mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RequiredMemberModelValidator.cs rename src/Microsoft.AspNet.Mvc.ModelBinding/{ => Validation}/SimpleTypesExcludeFilter.cs (97%) rename test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/{DataAnnotationsMetadataDetailsProviderTest.cs => DataAnnotationsMetadataProviderTest.cs} (92%) create mode 100644 test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataMemberBindingMetadataProviderTest.cs delete mode 100644 test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/AssociatedValidatorProviderTest.cs delete mode 100644 test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataMemberModelValidatorProviderTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DefaultModelValidatorProviderTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.TestCommon/TestModelValidatorProvider.cs diff --git a/src/Microsoft.AspNet.Mvc.Core/Controller.cs b/src/Microsoft.AspNet.Mvc.Core/Controller.cs index 3510736fb1..06d1c9414b 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Controller.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Controller.cs @@ -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; diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs index 705c062781..222f8b8cee 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs index f5dc3a3c7a..2e2497808d 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActionArgumentBinder.cs b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActionArgumentBinder.cs index ba0f384dd4..d9fdabe77a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActionArgumentBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActionArgumentBinder.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/DefaultValidationExcludeFiltersProvider.cs b/src/Microsoft.AspNet.Mvc.Core/DefaultValidationExcludeFiltersProvider.cs index 6a303e6dd6..4d8a6816e3 100644 --- a/src/Microsoft.AspNet.Mvc.Core/DefaultValidationExcludeFiltersProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/DefaultValidationExcludeFiltersProvider.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs index 80683d4c7b..ac08004838 100644 --- a/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/ResourceExecutingContext.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/ResourceExecutingContext.cs index 3618d1dcdd..0350f78566 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/ResourceExecutingContext.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/ResourceExecutingContext.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.ModelBinding.Validation; namespace Microsoft.AspNet.Mvc { diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/DefaultTypeBasedExcludeFilter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/DefaultTypeBasedExcludeFilter.cs index 2147a186f8..86dac927c5 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/DefaultTypeBasedExcludeFilter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/DefaultTypeBasedExcludeFilter.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/DefaultTypeNameBasedExcludeFilter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/DefaultTypeNameBasedExcludeFilter.cs index 5c2aef77df..25be7d9272 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/DefaultTypeNameBasedExcludeFilter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/DefaultTypeNameBasedExcludeFilter.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/Internal/ModelClientValidationRemoteRule.cs b/src/Microsoft.AspNet.Mvc.Core/Internal/ModelClientValidationRemoteRule.cs index 272ba952d6..36fb41d8e7 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Internal/ModelClientValidationRemoteRule.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Internal/ModelClientValidationRemoteRule.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinders/BodyModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinders/BodyModelBinder.cs index 7886446d0d..0d9d7aa7da 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinders/BodyModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinders/BodyModelBinder.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs index 4afa9f3bf0..86a4a7f8c4 100644 --- a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs @@ -84,7 +84,7 @@ namespace Microsoft.AspNet.Mvc /// /// Gets a list of which are used to construct a list - /// of exclude filters by . + /// of exclude filters by . /// public IList ValidationExcludeFilters { get; } @@ -115,7 +115,7 @@ namespace Microsoft.AspNet.Mvc /// /// Gets a list of the s used by - /// . + /// . /// public IList ModelValidatorProviders { get; } diff --git a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/DefaultModelValidatorProviderProvider.cs b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/DefaultModelValidatorProviderProvider.cs index 87cf7815a3..dd26fe75f2 100644 --- a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/DefaultModelValidatorProviderProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/DefaultModelValidatorProviderProvider.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ExcludeValidationDescriptor.cs b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ExcludeValidationDescriptor.cs index 8ab99ee608..9bdaf87dc8 100644 --- a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ExcludeValidationDescriptor.cs +++ b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ExcludeValidationDescriptor.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptor.cs b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptor.cs index 389ca67062..ca1ef139fa 100644 --- a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptor.cs +++ b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptor.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptorExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptorExtensions.cs index 8e40b20f69..2ee3317f8c 100644 --- a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptorExtensions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptorExtensions.cs @@ -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 to . /// /// A list of . - /// An instance. + /// An instance. /// A representing the added instance. public static ModelValidatorProviderDescriptor Add( [NotNull] this IList descriptors, @@ -75,7 +75,7 @@ namespace Microsoft.AspNet.Mvc /// A list of . /// The zero-based index at which /// should be inserted. - /// An instance. + /// An instance. /// A representing the added instance. public static ModelValidatorProviderDescriptor Insert( [NotNull] this IList descriptors, diff --git a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ValidationExcludeFiltersExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ValidationExcludeFiltersExtensions.cs index 09d346918c..0da40bf590 100644 --- a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ValidationExcludeFiltersExtensions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ValidationExcludeFiltersExtensions.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs index 775f1f7c3c..925f9f5e07 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.ModelBinding.Validation; namespace Microsoft.AspNet.Mvc { diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ModelBindingHelper.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ModelBindingHelper.cs index d27d5217ed..3b7a09de81 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ModelBindingHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ModelBindingHelper.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/RemoteAttribute.cs b/src/Microsoft.AspNet.Mvc.Core/RemoteAttribute.cs index d7c3060268..5de4ed2630 100644 --- a/src/Microsoft.AspNet.Mvc.Core/RemoteAttribute.cs +++ b/src/Microsoft.AspNet.Mvc.Core/RemoteAttribute.cs @@ -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; diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/DefaultHtmlGenerator.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/DefaultHtmlGenerator.cs index db10625be2..97443f5e05 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/DefaultHtmlGenerator.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/DefaultHtmlGenerator.cs @@ -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() .SelectMany(v => v.GetClientValidationRules(validationContext)); } diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs index 23b15cc092..8de7be9913 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs @@ -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; diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/IHtmlGenerator.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/IHtmlGenerator.cs index 6938d24ca0..4646b6e8ad 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/IHtmlGenerator.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/IHtmlGenerator.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs index 48cc8df723..c88702865a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs @@ -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; diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/UnobtrusiveValidationAttributesGenerator.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/UnobtrusiveValidationAttributesGenerator.cs index 04e28700a9..a2d769cf00 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/UnobtrusiveValidationAttributesGenerator.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/UnobtrusiveValidationAttributesGenerator.cs @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/MutableObjectModelBinder.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/MutableObjectModelBinder.cs index 3f6b1d99f1..d329c85376 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/MutableObjectModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/MutableObjectModelBinder.cs @@ -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; diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DataAnnotationsMetadataDetailsProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DataAnnotationsMetadataProvider.cs similarity index 93% rename from src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DataAnnotationsMetadataDetailsProvider.cs rename to src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DataAnnotationsMetadataProvider.cs index 45e10e522b..6ae291a1d2 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DataAnnotationsMetadataDetailsProvider.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DataAnnotationsMetadataProvider.cs @@ -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 and for /// the System.ComponentModel.DataAnnotations attribute classes. /// - public class DataAnnotationsMetadataDetailsProvider : + public class DataAnnotationsMetadataProvider : IBindingMetadataProvider, - IDisplayMetadataProvider + IDisplayMetadataProvider, + IValidationMetadataProvider { /// public void GetBindingMetadata([NotNull] BindingMetadataProviderContext context) @@ -167,5 +167,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata displayMetadata.TemplateHint = "HiddenInput"; } } + + /// + public void GetValidationMetadata([NotNull] ValidationMetadataProviderContext context) + { + foreach (var attribute in context.Attributes.OfType()) + { + context.ValidationMetadata.ValiatorMetadata.Add(attribute); + } + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DataMemberBindingMetadataProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DataMemberBindingMetadataProvider.cs new file mode 100644 index 0000000000..7d31b558b8 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DataMemberBindingMetadataProvider.cs @@ -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 +{ + /// + /// An for . + /// + public class DataMemberRequiredValidationMetadataProvider : IBindingMetadataProvider + { + /// + 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() + .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() != 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; + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultModelMetadata.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultModelMetadata.cs index c83a27c330..9ad5e8369b 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultModelMetadata.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultModelMetadata.cs @@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata private bool? _isReadOnly; private bool? _isRequired; private ModelPropertyCollection _properties; + private ReadOnlyCollection _validatorMetadata; /// /// Creates a new . @@ -356,5 +357,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata return DisplayMetadata.TemplateHint; } } + + /// + public override IReadOnlyList ValidatorMetadata + { + get + { + if (_validatorMetadata == null) + { + _validatorMetadata = new ReadOnlyCollection(ValidationMetadata.ValiatorMetadata); + } + + return _validatorMetadata; + } + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultModelMetadataProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultModelMetadataProvider.cs index ed7f748980..efa50bdb75 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultModelMetadataProvider.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultModelMetadataProvider.cs @@ -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(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(); } /// diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultValidationMetadataProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultValidationMetadataProvider.cs new file mode 100644 index 0000000000..cb6b919511 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/DefaultValidationMetadataProvider.cs @@ -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 +{ + /// + /// A default implementation of . + /// + public class DefaultValidationMetadataProvider : IValidationMetadataProvider + { + /// + public void GetValidationMetadata([NotNull] ValidationMetadataProviderContext context) + { + foreach (var attribute in context.Attributes.OfType()) + { + context.ValidationMetadata.ValiatorMetadata.Add(attribute); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/IMetadataDetailsProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/IMetadataDetailsProvider.cs index cf6f8831e4..9625bb423c 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/IMetadataDetailsProvider.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/IMetadataDetailsProvider.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata /// /// Marker interface for a provider of metadata details about model objects. Implementations should /// implement one or more of , , - /// and . + /// and . /// public interface IMetadataDetailsProvider { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/ValidationMetadata.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/ValidationMetadata.cs index 51acc5de88..51b8775a7e 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/ValidationMetadata.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/ValidationMetadata.cs @@ -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 { /// @@ -8,5 +10,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata /// public class ValidationMetadata { + /// + /// Gets a list of metadata items for validators. + /// + /// + /// implementations should store metadata items + /// in this list, to be consumed later by an . + /// + public IList ValiatorMetadata { get; } = new List(); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/ModelMetadata.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/ModelMetadata.cs index 4e7434679b..a166a0d505 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/ModelMetadata.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/ModelMetadata.cs @@ -199,6 +199,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding /// public abstract string TemplateHint { get; } + /// + /// Gets a collection of metadata items for validators. + /// + public abstract IReadOnlyList ValidatorMetadata { get;} + /// /// Gets a value indicating whether is a simple type. /// diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/OperationBindingContext.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/OperationBindingContext.cs index 49b8196abe..8160ec6fb2 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/OperationBindingContext.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/OperationBindingContext.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/AssociatedValidatorProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/AssociatedValidatorProvider.cs deleted file mode 100644 index 9a8b306b9a..0000000000 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/AssociatedValidatorProvider.cs +++ /dev/null @@ -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 GetValidators([NotNull] ModelMetadata metadata) - { - if (metadata.ContainerType != null && !string.IsNullOrEmpty(metadata.PropertyName)) - { - return GetValidatorsForProperty(metadata); - } - - return GetValidatorsForType(metadata); - } - - protected abstract IEnumerable GetValidators(ModelMetadata metadata, - IEnumerable attributes); - - private IEnumerable 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 GetValidatorsForType(ModelMetadata metadata) - { - var attributes = ModelAttributes.GetAttributesForType(metadata.ModelType); - - return GetValidators(metadata, attributes); - } - } -} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ClientModelValidationContext.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ClientModelValidationContext.cs index 3b2a0df500..3ef5653ff5 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ClientModelValidationContext.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ClientModelValidationContext.cs @@ -4,7 +4,7 @@ using System; using Microsoft.Framework.Internal; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class ClientModelValidationContext { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/CompareAttributeAdapter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/CompareAttributeAdapter.cs index 5c8f0093aa..2239c7d4a7 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/CompareAttributeAdapter.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/CompareAttributeAdapter.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/CompositeModelValidatorProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/CompositeModelValidatorProvider.cs index f949feabb6..ff4751429a 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/CompositeModelValidatorProvider.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/CompositeModelValidatorProvider.cs @@ -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 { /// /// Default implementation for . @@ -25,9 +24,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding public IReadOnlyList ValidatorProviders { get; } - public IEnumerable GetValidators(ModelMetadata metadata) + public void GetValidators(ModelValidatorProviderContext context) { - return ValidatorProviders.SelectMany(v => v.GetValidators(metadata)); + foreach (var validatorProvider in ValidatorProviders) + { + validatorProvider.GetValidators(context); + } } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidator.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidator.cs index 24d70780c4..f01b2c3d13 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidator.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidator.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidatorOfTAttribute.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidatorOfTAttribute.cs index e7c6d4abe9..653dc58b1a 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidatorOfTAttribute.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidatorOfTAttribute.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class DataAnnotationsModelValidator : DataAnnotationsModelValidator where TAttribute : ValidationAttribute diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidatorProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidatorProvider.cs index 1231db90ef..8487181e25 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidatorProvider.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidatorProvider.cs @@ -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 { /// /// An implementation of which providers validators @@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding /// . The logic to support /// is implemented in . /// - 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 _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 GetValidators(ModelMetadata metadata, - IEnumerable attributes) - { - var results = new List(); - - // Produce a validator for each validation attribute we find - foreach (var attribute in attributes.OfType()) + foreach (var attribute in context.ValidatorMetadata.OfType()) { 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 BuildAttributeFactoriesDictionary() diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataMemberModelValidatorProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataMemberModelValidatorProvider.cs deleted file mode 100644 index e5ecc1132a..0000000000 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataMemberModelValidatorProvider.cs +++ /dev/null @@ -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 -{ - /// - /// This provides a required ModelValidator for members marked - /// as [DataMember(IsRequired=true)]. - /// - public class DataMemberModelValidatorProvider : AssociatedValidatorProvider - { - protected override IEnumerable GetValidators(ModelMetadata metadata, - IEnumerable attributes) - { - // Types cannot be required; only properties can - if (metadata.ContainerType == null || string.IsNullOrEmpty(metadata.PropertyName)) - { - return Enumerable.Empty(); - } - - if (IsRequiredDataMember(metadata.ContainerType, attributes)) - { - return new[] { new RequiredMemberModelValidator() }; - } - - return Enumerable.Empty(); - } - - internal static bool IsRequiredDataMember(Type containerType, IEnumerable attributes) - { - var dataMemberAttribute = attributes.OfType() - .FirstOrDefault(); - if (dataMemberAttribute != null) - { - // isDataContract == true iff the container type has at least one DataContractAttribute - var isDataContract = containerType.GetTypeInfo() - .GetCustomAttributes() - .OfType() - .Any(); - if (isDataContract && dataMemberAttribute.IsRequired) - { - return true; - } - } - return false; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataTypeAttributeAdapter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataTypeAttributeAdapter.cs index d17384275e..ba6aad4407 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataTypeAttributeAdapter.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataTypeAttributeAdapter.cs @@ -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 { /// /// A validation adapter that is used to map 's to a single client side validation diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DefaultModelValidatorProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DefaultModelValidatorProvider.cs new file mode 100644 index 0000000000..9163b3aaee --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DefaultModelValidatorProvider.cs @@ -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 +{ + /// + /// A default . + /// + /// + /// The provides validators from + /// instances in . + /// + public class DefaultModelValidatorProvider : IModelValidatorProvider + { + /// + public void GetValidators(ModelValidatorProviderContext context) + { + foreach (var metadata in context.ValidatorMetadata) + { + var validator = metadata as IModelValidator; + if (validator != null) + { + context.Validators.Add(validator); + } + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DefaultObjectValidator.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DefaultObjectValidator.cs index 40cdae9bfc..952b596996 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DefaultObjectValidator.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DefaultObjectValidator.cs @@ -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 { /// /// Recursively validate an object. @@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding string modelKey, ModelExplorer modelExplorer, ValidationContext validationContext, - IEnumerable validators) + IList 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 validators) + IList 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) diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IClientModelValidator.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IClientModelValidator.cs index aee9e004a1..86267444d0 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IClientModelValidator.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IClientModelValidator.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public interface IClientModelValidator { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ICompositeModelValidatorProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ICompositeModelValidatorProvider.cs index df13a6dad8..96497865cd 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ICompositeModelValidatorProvider.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ICompositeModelValidatorProvider.cs @@ -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 { /// /// Represents aggregate of s that delegates to its underlying providers. diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IExcludeTypeValidationFilter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IExcludeTypeValidationFilter.cs index 4348a03ff2..ed5b40ac89 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IExcludeTypeValidationFilter.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IExcludeTypeValidationFilter.cs @@ -4,7 +4,7 @@ using System; using Microsoft.Framework.Internal; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { /// /// Provides an interface which is used to determine if s are excluded from model validation. diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidator.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidator.cs index 8f0994a85d..73722e3820 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidator.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidator.cs @@ -3,12 +3,26 @@ using System.Collections.Generic; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { + /// + /// Validates a model value. + /// public interface IModelValidator { + /// + /// Gets a value indicating whether or not this validator validates that a required value + /// has been provided for the model. + /// bool IsRequired { get; } + /// + /// Validates the model value. + /// + /// The . + /// + /// A list of indicating the results of validating the model value. + /// IEnumerable Validate(ModelValidationContext context); } } diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidatorProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidatorProvider.cs index 9a19d8faa3..4b082a2ed6 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidatorProvider.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidatorProvider.cs @@ -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 { + /// + /// Provides validators for a model value. + /// public interface IModelValidatorProvider { - IEnumerable GetValidators(ModelMetadata metadata); + /// + /// Gets the validators for . + /// + /// The . + /// + /// Implementations should add instances to + /// . + /// + void GetValidators(ModelValidatorProviderContext context); } } diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidatorProviderProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidatorProviderProvider.cs index 5bac7b77e2..da0ebb9ba9 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidatorProviderProvider.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidatorProviderProvider.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { /// /// Provides an activated collection of instances. diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/IObjectModelValidator.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IObjectModelValidator.cs similarity index 92% rename from src/Microsoft.AspNet.Mvc.ModelBinding/IObjectModelValidator.cs rename to src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IObjectModelValidator.cs index e9f98e12c4..73db126a8b 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/IObjectModelValidator.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IObjectModelValidator.cs @@ -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 { /// /// Provides methods to validate an object graph. diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IValidationExcludeFiltersProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IValidationExcludeFiltersProvider.cs index 3e6e14fec5..8874a76cd9 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IValidationExcludeFiltersProvider.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IValidationExcludeFiltersProvider.cs @@ -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 { /// /// Provides an activated collection of instances. diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/MaxLengthAttributeAdapter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/MaxLengthAttributeAdapter.cs index 52fb005ef5..d777f8f2db 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/MaxLengthAttributeAdapter.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/MaxLengthAttributeAdapter.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/MinLengthAttributeAdapter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/MinLengthAttributeAdapter.cs index dabb4082a5..bf6fcca7f1 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/MinLengthAttributeAdapter.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/MinLengthAttributeAdapter.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationEqualToRule.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationEqualToRule.cs index f4593cd452..0197955ae2 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationEqualToRule.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationEqualToRule.cs @@ -3,7 +3,7 @@ using Microsoft.Framework.Internal; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { /// /// Represents client side validation rule that determines if two values are equal. diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationMaxLengthRule.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationMaxLengthRule.cs index 0d3508d27c..4dd800f2a8 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationMaxLengthRule.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationMaxLengthRule.cs @@ -3,7 +3,7 @@ using Microsoft.Framework.Internal; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class ModelClientValidationMaxLengthRule : ModelClientValidationRule { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationMinLengthRule.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationMinLengthRule.cs index 2cce428431..c100096efc 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationMinLengthRule.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationMinLengthRule.cs @@ -3,7 +3,7 @@ using Microsoft.Framework.Internal; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class ModelClientValidationMinLengthRule : ModelClientValidationRule { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRangeRule.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRangeRule.cs index a060c33d96..ccc15de163 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRangeRule.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRangeRule.cs @@ -3,7 +3,7 @@ using Microsoft.Framework.Internal; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class ModelClientValidationRangeRule : ModelClientValidationRule { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRegexRule.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRegexRule.cs index 89067c81ad..0e795cfff8 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRegexRule.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRegexRule.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRequiredRule.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRequiredRule.cs index 8cc49d877e..8626c2a071 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRequiredRule.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRequiredRule.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRule.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRule.cs index 76e01a759d..71cf57d43f 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRule.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationRule.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationStringLengthRule.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationStringLengthRule.cs index 4f8d3f6db8..8e728bbf9f 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationStringLengthRule.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelClientValidationStringLengthRule.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValiatorProviderContext.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValiatorProviderContext.cs new file mode 100644 index 0000000000..a2a3830d66 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValiatorProviderContext.cs @@ -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 +{ + /// + /// A context for . + /// + public class ModelValidatorProviderContext + { + /// + /// Creates a new . + /// + /// The . + public ModelValidatorProviderContext(ModelMetadata modelMetadata) + { + ModelMetadata = modelMetadata; + } + + /// + /// Gets the . + /// + public ModelMetadata ModelMetadata { get; } + + /// + /// Gets the validator metadata. + /// + /// + /// This property provides convenience access to . + /// + public IReadOnlyList ValidatorMetadata + { + get + { + return ModelMetadata.ValidatorMetadata; + } + } + + /// + /// Gets the list of instances. instances + /// should add validators to this list when + /// + /// is called. + /// + public IList Validators { get; } = new List(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationContext.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationContext.cs index be35ba033c..b93cea0ffd 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationContext.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationContext.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationResult.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationResult.cs index 64459bea03..5ed1b0f5df 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationResult.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationResult.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RangeAttributeAdapter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RangeAttributeAdapter.cs index b669bb2c7e..b83cb2c7c9 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RangeAttributeAdapter.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RangeAttributeAdapter.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ReferenceEqualityComparer.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ReferenceEqualityComparer.cs index 9f3fd19edd..699552f2f1 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ReferenceEqualityComparer.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ReferenceEqualityComparer.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RegularExpressionAttributeAdapter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RegularExpressionAttributeAdapter.cs index 4ba165e5ea..094ff2feb3 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RegularExpressionAttributeAdapter.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RegularExpressionAttributeAdapter.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RequiredAttributeAdapter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RequiredAttributeAdapter.cs index 9e1bf17e88..fb6940634f 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RequiredAttributeAdapter.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RequiredAttributeAdapter.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RequiredMemberModelValidator.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RequiredMemberModelValidator.cs deleted file mode 100644 index 2b8bcac6e2..0000000000 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/RequiredMemberModelValidator.cs +++ /dev/null @@ -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 Validate(ModelValidationContext context) - { - return Enumerable.Empty(); - } - } -} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/SimpleTypesExcludeFilter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/SimpleTypesExcludeFilter.cs similarity index 97% rename from src/Microsoft.AspNet.Mvc.ModelBinding/SimpleTypesExcludeFilter.cs rename to src/Microsoft.AspNet.Mvc.ModelBinding/Validation/SimpleTypesExcludeFilter.cs index 84e31037ce..0d9a76c786 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/SimpleTypesExcludeFilter.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/SimpleTypesExcludeFilter.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Reflection; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { /// /// Identifies the simple types that the default model binding validation will exclude. diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/StringLengthAttributeAdapter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/StringLengthAttributeAdapter.cs index 5b151e3acf..dc18c738d2 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/StringLengthAttributeAdapter.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/StringLengthAttributeAdapter.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ValidatableObjectAdapter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ValidatableObjectAdapter.cs index 0f99b90ce1..9da152887c 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ValidatableObjectAdapter.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ValidatableObjectAdapter.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs index 6ae87ee318..b80688de9a 100644 --- a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs @@ -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; diff --git a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs index 2b642ea8cd..5a228295dc 100644 --- a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs +++ b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs @@ -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()); diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index 08f691871c..f14ad95f8a 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -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; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/BodyModelBinderTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/BodyModelBinderTests.cs index 7b6f477e8e..1cbcfb3762 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/BodyModelBinderTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/BodyModelBinderTests.cs @@ -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; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs index 02993b33c3..cca14ad33a 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs @@ -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; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs index 662f0b231d..b92c3a665c 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs @@ -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(); - provider.Setup(v => v.GetValidators(It.IsAny())) - .Returns(new[] { validator1.Object }); + provider.Setup(v => v.GetValidators(It.IsAny())) + .Callback(c => c.Validators.Add(validator1.Object)); var binder = new Mock(); var controller = GetController(binder.Object, provider: null); @@ -1613,8 +1614,8 @@ namespace Microsoft.AspNet.Mvc.Test .Returns(validationResult); var provider = new Mock(); - provider.Setup(v => v.GetValidators(It.IsAny())) - .Returns(new[] { validator1.Object }); + provider.Setup(v => v.GetValidators(It.IsAny())) + .Callback(c => c.Validators.Add(validator1.Object)); var binder = new Mock(); var controller = GetController(binder.Object, provider: null); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs index 6f222a2037..50b458c603 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs @@ -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; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/MockModelValidatorProviderProvider.cs b/test/Microsoft.AspNet.Mvc.Core.Test/MockModelValidatorProviderProvider.cs index 8c9b8612df..bd7877b13b 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/MockModelValidatorProviderProvider.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/MockModelValidatorProviderProvider.cs @@ -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 ModelValidatorProviders { get; } = new List(); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/DefaultValidationProviderProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/DefaultValidationProviderProviderTest.cs index af31c9f994..c8c8f021ce 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/DefaultValidationProviderProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/DefaultValidationProviderProviderTest.cs @@ -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 GetValidators(ModelMetadata metadata) + public void GetValidators(ModelValidatorProviderContext context) { - throw new NotImplementedException(); } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ExcludeValidationDescriptorTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ExcludeValidationDescriptorTests.cs index db872c7b50..aa7bd538ea 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ExcludeValidationDescriptorTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ExcludeValidationDescriptorTests.cs @@ -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 diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptor.cs b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptor.cs index 34c583dd3a..ad335b9247 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptor.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptor.cs @@ -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 GetValidators(ModelMetadata metadata) + public void GetValidators(ModelValidatorProviderContext context) { - throw new NotImplementedException(); } } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptorExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptorExtensionsTest.cs index c89752ff33..882116f31b 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptorExtensionsTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptorExtensionsTest.cs @@ -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(); var provider2 = Mock.Of(); - var type1 = typeof(DataMemberModelValidatorProvider); + var type1 = typeof(DefaultModelValidatorProvider); var type2 = typeof(DataAnnotationsModelValidatorProvider); var collection = new List(); @@ -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(collection[2].OptionType); + Assert.IsType(collection[2].OptionType); Assert.IsType(collection[3].OptionType); } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ControllerActionArgumentBinderTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ControllerActionArgumentBinderTests.cs index 0c8b04b043..114b58e656 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ControllerActionArgumentBinderTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ControllerActionArgumentBinderTests.cs @@ -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; } } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ModelBindingHelperTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ModelBindingHelperTest.cs index 32d474f71f..d09711de9b 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ModelBindingHelperTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ModelBindingHelperTest.cs @@ -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 { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/RemoteAttributeTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/RemoteAttributeTest.cs index bd7b4aae7e..6117032608 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/RemoteAttributeTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/RemoteAttributeTest.cs @@ -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; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs index 3f70d7e464..467d9ab9c5 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs @@ -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; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs index ac9991a245..4f2962afab 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs @@ -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; diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/BinderTypeBasedModelBinderModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/BinderTypeBasedModelBinderModelBinderTest.cs index 45beefa362..435d5596cc 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/BinderTypeBasedModelBinderModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/BinderTypeBasedModelBinderModelBinderTest.cs @@ -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 { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs index 5fc3692480..f36e6ee137 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs @@ -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(); - provider.Setup(v => v.GetValidators(It.IsAny())) - .Returns(validators ?? Enumerable.Empty()); + provider + .Setup(v => v.GetValidators(It.IsAny())) + .Callback(c => + { + if (validators == null) + { + return; + } + + foreach (var validator in validators) + { + c.Validators.Add(validator); + } + }); return provider.Object; } diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs index 4335ec30c9..9655ef8a40 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs @@ -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; diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/MutableObjectModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/MutableObjectModelBinderTest.cs index 4039322c45..20f6599def 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/MutableObjectModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/MutableObjectModelBinderTest.cs @@ -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) diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataAnnotationsMetadataDetailsProviderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataAnnotationsMetadataProviderTest.cs similarity index 92% rename from test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataAnnotationsMetadataDetailsProviderTest.cs rename to test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataAnnotationsMetadataProviderTest.cs index 8d567a3f13..f684f7164e 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataAnnotationsMetadataDetailsProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataAnnotationsMetadataProviderTest.cs @@ -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> 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() { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataMemberBindingMetadataProviderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataMemberBindingMetadataProviderTest.cs new file mode 100644 index 0000000000..8c41a59c31 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataMemberBindingMetadataProviderTest.cs @@ -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; } + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DefaultModelMetadataProviderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DefaultModelMetadataProviderTest.cs index 24fc89db0b..885a096849 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DefaultModelMetadataProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DefaultModelMetadataProviderTest.cs @@ -110,6 +110,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata Assert.Equal("OnPropertyType", Assert.IsType(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; } + } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataProviderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataProviderTest.cs index 5f11ea44c3..91ebb4e296 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataProviderTest.cs @@ -701,7 +701,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata : base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[] { new DefaultBindingMetadataProvider(), - new DataAnnotationsMetadataDetailsProvider(), + new DataAnnotationsMetadataProvider(), })) { _attributes = attributes; diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs index 0c4b72aee3..4da7aaf73b 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs @@ -372,6 +372,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding throw new NotImplementedException(); } } + + public override IReadOnlyList ValidatorMetadata + { + get + { + throw new NotImplementedException(); + } + } } } } diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/SimpleTypeExcludeFilterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/SimpleTypeExcludeFilterTest.cs index 5918bc629d..e697585708 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/SimpleTypeExcludeFilterTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/SimpleTypeExcludeFilterTest.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding.Test +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class SimpleTypeExcluceFilterTest { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/AssociatedValidatorProviderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/AssociatedValidatorProviderTest.cs deleted file mode 100644 index 35a9d52fcc..0000000000 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/AssociatedValidatorProviderTest.cs +++ /dev/null @@ -1,103 +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. - -#if DNX451 -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using Moq; -using Xunit; - -namespace Microsoft.AspNet.Mvc.ModelBinding -{ - public class AssociatedValidatorProviderTest - { - private readonly IModelMetadataProvider _metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); - - [Fact] - public void GetValidatorsForPropertyWithLocalAttributes() - { - // Arrange - IEnumerable callbackAttributes = null; - var metadata = _metadataProvider.GetMetadataForProperty(typeof(PropertyModel), "LocalAttributes"); - var provider = new Mock { CallBase = true }; - provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny>())) - .Callback>((m, attributes) => callbackAttributes = attributes) - .Returns((IEnumerable)null) - .Verifiable(); - - // Act - provider.Object.GetValidators(metadata); - - // Assert - provider.Verify(); - Assert.True(callbackAttributes.Any(a => a is RequiredAttribute)); - } - - [Fact] - public void GetValidatorsForPropertyWithMetadataAttributes() - { - // Arrange - IEnumerable callbackAttributes = null; - var metadata = _metadataProvider.GetMetadataForProperty(typeof(PropertyModel), "MetadataAttributes"); - var provider = new Mock { CallBase = true }; - provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny>())) - .Callback>((m, attributes) => callbackAttributes = attributes) - .Returns((IEnumerable)null) - .Verifiable(); - - // Act - provider.Object.GetValidators(metadata); - - // Assert - provider.Verify(); - Assert.True(callbackAttributes.Any(a => a is RangeAttribute)); - } - - [Fact] - public void GetValidatorsForPropertyWithMixedAttributes() - { - // Arrange - IEnumerable callbackAttributes = null; - var metadata = _metadataProvider.GetMetadataForProperty(typeof(PropertyModel), "MixedAttributes"); - var provider = new Mock { CallBase = true }; - provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny>())) - .Callback>((m, attributes) => callbackAttributes = attributes) - .Returns((IEnumerable)null) - .Verifiable(); - - // Act - provider.Object.GetValidators(metadata); - - // Assert - provider.Verify(); - Assert.True(callbackAttributes.Any(a => a is RangeAttribute)); - Assert.True(callbackAttributes.Any(a => a is RequiredAttribute)); - } - - private class PropertyModel - { - [Required] - public int LocalAttributes { get; set; } - - [Range(10, 100)] - public string MetadataAttributes { get; set; } - - [Required] - [Range(10, 100)] - public double MixedAttributes { get; set; } - } - - public abstract class TestableAssociatedValidatorProvider : AssociatedValidatorProvider - { - protected override IEnumerable GetValidators(ModelMetadata metadata, IEnumerable attributes) - { - return AbstractGetValidators(metadata, attributes); - } - - public abstract IEnumerable AbstractGetValidators(ModelMetadata metadata, IEnumerable attributes); - } - } -} -#endif diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompareAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompareAttributeAdapterTest.cs index 0a0d647084..40b3447211 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompareAttributeAdapterTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompareAttributeAdapterTest.cs @@ -6,7 +6,7 @@ using Microsoft.AspNet.Testing; using Microsoft.Framework.DependencyInjection; using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class CompareAttributeAdapterTest { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompositeModelValidatorProviderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompositeModelValidatorProviderTest.cs index 4132b0194f..b500d1faf5 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompositeModelValidatorProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompositeModelValidatorProviderTest.cs @@ -2,10 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if DNX451 +using System.Linq; using Moq; using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding.Test +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class CompositeModelValidatorProviderTest { @@ -16,20 +17,33 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test var validator1 = Mock.Of(); var validator2 = Mock.Of(); var validator3 = Mock.Of(); + var provider1 = new Mock(); - provider1.Setup(p => p.GetValidators(It.IsAny())) - .Returns(new[] { validator1, validator2 }); + provider1.Setup(p => p.GetValidators(It.IsAny())) + .Callback(c => + { + c.Validators.Add(validator1); + c.Validators.Add(validator2); + }); + var provider2 = new Mock(); - provider2.Setup(p => p.GetValidators(It.IsAny())) - .Returns(new[] { validator3 }); + provider2.Setup(p => p.GetValidators(It.IsAny())) + .Callback(c => + { + c.Validators.Add(validator3); + }); + var compositeModelValidator = new CompositeModelValidatorProvider(new[] { provider1.Object, provider2.Object }); var modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(string)); // Act - var result = compositeModelValidator.GetValidators(modelMetadata); + var validatorProviderContext = new ModelValidatorProviderContext(modelMetadata); + compositeModelValidator.GetValidators(validatorProviderContext); // Assert - Assert.Equal(new[] { validator1, validator2, validator3 }, result); + Assert.Equal( + new[] { validator1, validator2, validator3 }, + validatorProviderContext.Validators.ToArray()); } } } diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorProviderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorProviderTest.cs index 344dec6052..47bc1184ac 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorProviderTest.cs @@ -10,7 +10,7 @@ using Moq; #endif using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class DataAnnotationsModelValidatorProviderTest { @@ -25,11 +25,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding var mockValidatable = Mock.Of(); var metadata = _metadataProvider.GetMetadataForType(mockValidatable.GetType()); + var providerContext = new ModelValidatorProviderContext(metadata); + // Act - var validators = provider.GetValidators(metadata); + provider.GetValidators(providerContext); // Assert - var validator = Assert.Single(validators); + var validator = Assert.Single(providerContext.Validators); Assert.IsType(validator); } #endif @@ -42,11 +44,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding var metadata = _metadataProvider.GetMetadataForProperty(typeof(DummyRequiredAttributeHelperClass), "WithAttribute"); + var providerContext = new ModelValidatorProviderContext(metadata); + // Act - var validators = provider.GetValidators(metadata); + provider.GetValidators(providerContext); // Assert - var validator = Assert.Single(validators); + var validator = Assert.Single(providerContext.Validators); var adapter = Assert.IsType(validator); Assert.Equal("Custom Required Message", adapter.Attribute.ErrorMessage); } @@ -144,11 +148,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding var provider = new DataAnnotationsModelValidatorProvider(); var metadata = _metadataProvider.GetMetadataForType(typeof(DummyClassWithDummyValidationAttribute)); + var providerContext = new ModelValidatorProviderContext(metadata); + // Act - IEnumerable validators = provider.GetValidators(metadata); + provider.GetValidators(providerContext); // Assert - var validator = validators.Single(); + var validator = providerContext.Validators.Single(); Assert.IsType(validator); } @@ -172,42 +178,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding var mockValidatable = new Mock(); var metadata = _metadataProvider.GetMetadataForType(mockValidatable.Object.GetType()); + var providerContext = new ModelValidatorProviderContext(metadata); + // Act - var validators = provider.GetValidators(metadata); + provider.GetValidators(providerContext); // Assert - Assert.Single(validators); + Assert.Single(providerContext.Validators); } #endif - // Integration with metadata system - - [Fact] - public void DoesNotReadPropertyValue() - { - // Arrange - var provider = new DataAnnotationsModelValidatorProvider(); - var model = new ObservableModel(); - - var modelExplorer = _metadataProvider - .GetModelExplorerForType(typeof(ObservableModel), model) - .GetExplorerForProperty("TheProperty"); - - var context = new ModelValidationContext( - rootPrefix: null, - validatorProvider: null, - modelState: null, - modelExplorer: modelExplorer); - - // Act - var validators = provider.GetValidators(modelExplorer.Metadata).ToArray(); - var results = validators.SelectMany(o => o.Validate(context)).ToArray(); - - // Assert - Assert.Empty(validators); - Assert.False(model.PropertyWasRead()); - } - private class ObservableModel { private bool _propertyWasRead; diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorTest.cs index 61b8bd1508..7a594ea85e 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorTest.cs @@ -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; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; #if DNX451 @@ -14,7 +13,7 @@ using Moq.Protected; #endif using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class DataAnnotationsModelValidatorTest { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataMemberModelValidatorProviderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataMemberModelValidatorProviderTest.cs deleted file mode 100644 index 96af57fe73..0000000000 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataMemberModelValidatorProviderTest.cs +++ /dev/null @@ -1,95 +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.Runtime.Serialization; -using Xunit; - -namespace Microsoft.AspNet.Mvc.ModelBinding -{ - public class DataMemberModelValidatorProviderTest - { - private readonly IModelMetadataProvider _metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); - - [Fact] - public void ClassWithoutAttributes_NoValidator() - { - // Arrange - var provider = new DataMemberModelValidatorProvider(); - var metadata = _metadataProvider.GetMetadataForProperty(typeof(ClassWithoutAttributes), "TheProperty"); - - // Act - var validators = provider.GetValidators(metadata); - - // Assert - Assert.Empty(validators); - } - - private class ClassWithoutAttributes - { - public int TheProperty { get; set; } - } - - [Fact] - public void ClassWithDataMemberIsRequiredTrue_Validator() - { - // Arrange - var provider = new DataMemberModelValidatorProvider(); - var metadata = _metadataProvider.GetMetadataForProperty(typeof(ClassWithDataMemberIsRequiredTrue), "TheProperty"); - - // Act - var validators = provider.GetValidators(metadata); - - // Assert - var validator = Assert.Single(validators); - Assert.True(validator.IsRequired); - } - - [DataContract] - private class ClassWithDataMemberIsRequiredTrue - { - [DataMember(IsRequired = true)] - public int TheProperty { get; set; } - } - - [Fact] - public void ClassWithDataMemberIsRequiredFalse_NoValidator() - { - // Arrange - var provider = new DataMemberModelValidatorProvider(); - var metadata = _metadataProvider.GetMetadataForProperty(typeof(ClassWithDataMemberIsRequiredFalse), "TheProperty"); - - // Act - var validators = provider.GetValidators(metadata); - - // Assert - Assert.Empty(validators); - } - - [DataContract] - private class ClassWithDataMemberIsRequiredFalse - { - [DataMember(IsRequired = false)] - public int TheProperty { get; set; } - } - - [Fact] - public void ClassWithDataMemberIsRequiredTrueWithoutDataContract_NoValidator() - { - // Arrange - var provider = new DataMemberModelValidatorProvider(); - var metadata = _metadataProvider.GetMetadataForProperty(typeof(ClassWithDataMemberIsRequiredTrueWithoutDataContract), "TheProperty"); - - // Act - var validators = provider.GetValidators(metadata); - - // Assert - Assert.Empty(validators); - } - - private class ClassWithDataMemberIsRequiredTrueWithoutDataContract - { - [DataMember(IsRequired = true)] - public int TheProperty { get; set; } - } - } -} diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DefaultModelValidatorProviderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DefaultModelValidatorProviderTest.cs new file mode 100644 index 0000000000..cdab98fcdb --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DefaultModelValidatorProviderTest.cs @@ -0,0 +1,264 @@ +// 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.ComponentModel.DataAnnotations; +using Xunit; + +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation +{ + // Integration tests for the default configuration of ModelMetadata and Validation providers + public class DefaultModelValidatorProviderTest + { + [Fact] + public void GetValidators_ForIValidatableObject() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider(); + + var metadata = metadataProvider.GetMetadataForType(typeof(ValidatableObject)); + var context = new ModelValidatorProviderContext(metadata); + + // Act + validatorProvider.GetValidators(context); + + // Assert + var validators = context.Validators; + + var validator = Assert.Single(validators); + Assert.IsType(validator); + } + + [Fact] + public void GetValidators_ModelValidatorAttributeOnClass() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider(); + + var metadata = metadataProvider.GetMetadataForType(typeof(ModelValidatorAttributeOnClass)); + var context = new ModelValidatorProviderContext(metadata); + + // Act + validatorProvider.GetValidators(context); + + // Assert + var validators = context.Validators; + + var validator = Assert.IsType(Assert.Single(validators)); + Assert.Equal("Class", validator.Tag); + } + + [Fact] + public void GetValidators_ModelValidatorAttributeOnProperty() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider(); + + var metadata = metadataProvider.GetMetadataForProperty( + typeof(ModelValidatorAttributeOnProperty), + nameof(ModelValidatorAttributeOnProperty.Property)); + var context = new ModelValidatorProviderContext(metadata); + + // Act + validatorProvider.GetValidators(context); + + // Assert + var validators = context.Validators; + + var validator = Assert.IsType(Assert.Single(validators)); + Assert.Equal("Property", validator.Tag); + } + + [Fact] + public void GetValidators_ModelValidatorAttributeOnPropertyAndClass() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider(); + + var metadata = metadataProvider.GetMetadataForProperty( + typeof(ModelValidatorAttributeOnPropertyAndClass), + nameof(ModelValidatorAttributeOnPropertyAndClass.Property)); + var context = new ModelValidatorProviderContext(metadata); + + // Act + validatorProvider.GetValidators(context); + + // Assert + var validators = context.Validators; + + Assert.Equal(2, validators.Count); + Assert.Single(validators, v => Assert.IsType(v).Tag == "Class"); + Assert.Single(validators, v => Assert.IsType(v).Tag == "Property"); + } + + // RangeAttribute is an example of a ValidationAttribute with it's own adapter type. + [Fact] + public void GetValidators_DataAnnotationsAttribute_SpecificAdapter() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider(); + + var metadata = metadataProvider.GetMetadataForProperty( + typeof(RangeAttributeOnProperty), + nameof(RangeAttributeOnProperty.Property)); + var context = new ModelValidatorProviderContext(metadata); + + // Act + validatorProvider.GetValidators(context); + + // Assert + var validators = context.Validators; + + Assert.IsType(Assert.Single(validators)); + } + + [Fact] + public void GetValidators_DataAnnotationsAttribute_DefaultAdapter() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider(); + + var metadata = metadataProvider.GetMetadataForProperty( + typeof(CustomValidationAttributeOnProperty), + nameof(CustomValidationAttributeOnProperty.Property)); + var context = new ModelValidatorProviderContext(metadata); + + // Act + validatorProvider.GetValidators(context); + + // Assert + var validators = context.Validators; + + Assert.IsType(Assert.Single(validators)); + } + + [Fact] + public void GetValidators_FromModelMetadataType_SingleValidator() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider(); + + var metadata = metadataProvider.GetMetadataForProperty( + typeof(ProductViewModel), + nameof(ProductViewModel.Id)); + var context = new ModelValidatorProviderContext(metadata); + + // Act + validatorProvider.GetValidators(context); + + // Assert + var validators = context.Validators; + + Assert.IsType(Assert.Single(validators)); + } + + [Fact] + public void GetValidators_FromModelMetadataType_MergedValidators() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider(); + + var metadata = metadataProvider.GetMetadataForProperty( + typeof(ProductViewModel), + nameof(ProductViewModel.Name)); + var context = new ModelValidatorProviderContext(metadata); + + // Act + validatorProvider.GetValidators(context); + + // Assert + var validators = context.Validators; + + Assert.Equal(2, validators.Count); + Assert.Single(validators, v => v is RegularExpressionAttributeAdapter); + Assert.Single(validators, v => v is StringLengthAttributeAdapter); + } + + private class ValidatableObject : IValidatableObject + { + public IEnumerable Validate(ValidationContext validationContext) + { + return null; + } + } + + [CustomModelValidator(Tag = "Class")] + private class ModelValidatorAttributeOnClass + { + } + + + private class ModelValidatorAttributeOnProperty + { + [CustomModelValidator(Tag = "Property")] + public string Property { get; set; } + } + + private class ModelValidatorAttributeOnPropertyAndClass + { + [CustomModelValidator(Tag = "Property")] + public ModelValidatorAttributeOnClass Property { get; set; } + } + + private class CustomModelValidatorAttribute : Attribute, IModelValidator + { + public string Tag { get; set; } + + public bool IsRequired + { + get + { + throw new NotImplementedException(); + } + } + + public IEnumerable Validate(ModelValidationContext context) + { + throw new NotImplementedException(); + } + } + + private class RangeAttributeOnProperty + { + [Range(0, 10)] + public int Property { get; set; } + } + + private class CustomValidationAttribute : ValidationAttribute + { + } + + private class CustomValidationAttributeOnProperty + { + [CustomValidation] + public int Property { get; set; } + } + + private class ProductEntity + { + [Range(0, 10)] + public int Id { get; set; } + + [RegularExpression(".*")] + public string Name { get; set; } + } + + [ModelMetadataType(typeof(ProductEntity))] + private class ProductViewModel + { + public int Id { get; set; } + + [StringLength(4)] + public string Name { get; set; } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DefaultObjectValidatorTests.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DefaultObjectValidatorTests.cs index 3715fe3337..9b983d589a 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DefaultObjectValidatorTests.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DefaultObjectValidatorTests.cs @@ -5,16 +5,12 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Globalization; -using System.IO; using System.Linq; -using System.Xml; -using System.Xml.Linq; using Microsoft.AspNet.Testing; using Moq; using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class DefaultObjectValidatorTests { @@ -489,12 +485,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { var modelStateDictionary = new ModelStateDictionary(); - var providers = new IModelValidatorProvider[] - { - new DataAnnotationsModelValidatorProvider(), - new DataMemberModelValidatorProvider() - }; - var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var excludedValidationTypesPredicate = new List(); @@ -519,7 +509,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { ModelValidationContext = new ModelValidationContext( key, - new CompositeModelValidatorProvider(providers), + TestModelValidatorProvider.CreateDefaultProvider(), modelStateDictionary, modelExplorer), ModelMetadataProvider = modelMetadataProvider, diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MaxLengthAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MaxLengthAttributeAdapterTest.cs index 55e6bd8bf1..6a39915f1f 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MaxLengthAttributeAdapterTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MaxLengthAttributeAdapterTest.cs @@ -6,7 +6,7 @@ using Microsoft.AspNet.Testing; using Microsoft.Framework.DependencyInjection; using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class MaxLengthAttributeAdapterTest { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MinLengthAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MinLengthAttributeAdapterTest.cs index b655485bad..847b4d7668 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MinLengthAttributeAdapterTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MinLengthAttributeAdapterTest.cs @@ -6,7 +6,7 @@ using Microsoft.AspNet.Testing; using Microsoft.Framework.DependencyInjection; using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class MinLengthAttributeAdapterTest { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RangeAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RangeAttributeAdapterTest.cs index 0dcf505634..58a87bec3e 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RangeAttributeAdapterTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RangeAttributeAdapterTest.cs @@ -6,7 +6,7 @@ using Microsoft.AspNet.Testing; using Microsoft.Framework.DependencyInjection; using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class RangeAttributeAdapterTest { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/ReferenceEqualityComparerTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/ReferenceEqualityComparerTest.cs index bdd5398ca5..82a55d73e8 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/ReferenceEqualityComparerTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/ReferenceEqualityComparerTest.cs @@ -4,7 +4,7 @@ using System; using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class ReferenceEqualityComparerTest { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RequiredAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RequiredAttributeAdapterTest.cs index 03dbbe0146..d5391ade8b 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RequiredAttributeAdapterTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RequiredAttributeAdapterTest.cs @@ -6,7 +6,7 @@ using Microsoft.AspNet.Testing; using Microsoft.Framework.DependencyInjection; using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class RequiredAttributeAdapterTest { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/StringLengthAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/StringLengthAttributeAdapterTest.cs index 36243d40f6..9bc1f11e86 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/StringLengthAttributeAdapterTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/StringLengthAttributeAdapterTest.cs @@ -6,7 +6,7 @@ using Microsoft.AspNet.Testing; using Microsoft.Framework.DependencyInjection; using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc.ModelBinding.Validation { public class StringLengthAttributeAdapterTest { diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs index 3f16a5ad2d..6328a70e30 100644 --- a/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs +++ b/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs @@ -4,6 +4,7 @@ using System; using System.Xml.Linq; using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Microsoft.AspNet.Mvc.Razor; using Newtonsoft.Json.Linq; using Xunit; @@ -118,8 +119,8 @@ namespace Microsoft.AspNet.Mvc // Assert Assert.Equal(2, mvcOptions.ModelValidatorProviders.Count); - Assert.IsType(mvcOptions.ModelValidatorProviders[0].Instance); - Assert.IsType(mvcOptions.ModelValidatorProviders[1].Instance); + Assert.IsType(mvcOptions.ModelValidatorProviders[0].Instance); + Assert.IsType(mvcOptions.ModelValidatorProviders[1].Instance); } [Fact] diff --git a/test/Microsoft.AspNet.Mvc.TestCommon/TestModelMetadataProvider.cs b/test/Microsoft.AspNet.Mvc.TestCommon/TestModelMetadataProvider.cs index 6b18cb7a70..ab27911a72 100644 --- a/test/Microsoft.AspNet.Mvc.TestCommon/TestModelMetadataProvider.cs +++ b/test/Microsoft.AspNet.Mvc.TestCommon/TestModelMetadataProvider.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Reflection; using Microsoft.AspNet.Mvc.ModelBinding.Metadata; -using Microsoft.Framework.Internal; using Xunit; namespace Microsoft.AspNet.Mvc.ModelBinding @@ -18,7 +17,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding var detailsProviders = new IMetadataDetailsProvider[] { new DefaultBindingMetadataProvider(), - new DataAnnotationsMetadataDetailsProvider(), + new DefaultValidationMetadataProvider(), + new DataAnnotationsMetadataProvider(), + new DataMemberRequiredValidationMetadataProvider(), }; var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders); @@ -36,7 +37,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding : base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[] { new DefaultBindingMetadataProvider(), - new DataAnnotationsMetadataDetailsProvider(), + new DefaultValidationMetadataProvider(), + new DataAnnotationsMetadataProvider(), detailsProvider })) { diff --git a/test/Microsoft.AspNet.Mvc.TestCommon/TestModelValidatorProvider.cs b/test/Microsoft.AspNet.Mvc.TestCommon/TestModelValidatorProvider.cs new file mode 100644 index 0000000000..3a141d908f --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.TestCommon/TestModelValidatorProvider.cs @@ -0,0 +1,27 @@ +// 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 +{ + internal class TestModelValidatorProvider : CompositeModelValidatorProvider + { + // Creates a provider with all the defaults - includes data annotations + public static IModelValidatorProvider CreateDefaultProvider() + { + var providers = new IModelValidatorProvider[] + { + new DefaultModelValidatorProvider(), + new DataAnnotationsModelValidatorProvider(), + }; + + return new TestModelValidatorProvider(providers); + } + + public TestModelValidatorProvider(IEnumerable providers) + : base(providers) + { + } + } +} \ No newline at end of file diff --git a/test/WebSites/ModelBindingWebSite/Controllers/VehicleController.cs b/test/WebSites/ModelBindingWebSite/Controllers/VehicleController.cs index c130d226fa..956c906c0b 100644 --- a/test/WebSites/ModelBindingWebSite/Controllers/VehicleController.cs +++ b/test/WebSites/ModelBindingWebSite/Controllers/VehicleController.cs @@ -37,7 +37,7 @@ namespace ModelBindingWebSite [HttpPut("/api/vehicles/{id}")] [Produces("application/json")] public object UpdateVehicleApi( - [Range(1, 500)] int id, + int id, [FromBody] VehicleViewModel model, [FromServices] IVehicleService service, [FromHeader(Name = "X-TrackingId")] string trackingId)