diff --git a/src/Microsoft.AspNet.Mvc.Core/Controller.cs b/src/Microsoft.AspNet.Mvc.Core/Controller.cs index 700784dfc5..da5b5d98f4 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Controller.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Controller.cs @@ -526,7 +526,7 @@ public virtual async Task TryUpdateModelAsync([NotNull] TModel mod bindingContext.MetadataProvider, bindingContext.ModelBinder, valueProvider, - bindingContext.ValidatorProviders); + bindingContext.ValidatorProvider); } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs index d30b8c7b71..1ae18ae5d5 100644 --- a/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs @@ -260,7 +260,7 @@ internal async Task> GetActionArguments(ModelStateDi ModelMetadata = modelMetadata, ModelBinder = actionBindingContext.ModelBinder, ValueProvider = actionBindingContext.ValueProvider, - ValidatorProviders = actionBindingContext.ValidatorProviders, + ValidatorProvider = actionBindingContext.ValidatorProvider, MetadataProvider = metadataProvider, HttpContext = actionBindingContext.ActionContext.HttpContext, FallbackToEmptyPrefix = true diff --git a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs index bf56da758e..815324c60c 100644 --- a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs @@ -92,6 +92,12 @@ public RazorViewEngineOptions ViewEngineOptions /// public List ModelBinders { get; private set; } + /// + /// Get a list of the used by this application. + /// + public List ModelValidatorProviders { get; } = + new List(); + /// /// Gets a list of descriptors that represent used /// by this application. diff --git a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/DefaultModelValidatorProviderProvider.cs b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/DefaultModelValidatorProviderProvider.cs new file mode 100644 index 0000000000..466333395f --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/DefaultModelValidatorProviderProvider.cs @@ -0,0 +1,37 @@ +// 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 Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.OptionsModel; + +namespace Microsoft.AspNet.Mvc.OptionDescriptors +{ + /// + public class DefaultModelValidatorProviderProvider : OptionDescriptorBasedProvider, + IModelValidatorProviderProvider + { + /// + /// Initializes a new instance of the class. + /// + /// An accessor to the configured for this application. + /// An instance used to instantiate types. + /// A instance that retrieves services from the + /// service collection. + public DefaultModelValidatorProviderProvider( + IOptionsAccessor optionsAccessor, + ITypeActivator typeActivator, + IServiceProvider serviceProvider) + : base(optionsAccessor.Options.ModelValidatorProviders, typeActivator, serviceProvider) + { + } + + /// + public IReadOnlyList ModelValidatorProviders + { + get { return Options; } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptor.cs b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptor.cs new file mode 100644 index 0000000000..720d3139e7 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptor.cs @@ -0,0 +1,32 @@ +// 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; + +namespace Microsoft.AspNet.Mvc.OptionDescriptors +{ + /// + /// Encapsulates information that describes an . + /// + public class ModelValidatorProviderDescriptor : OptionDescriptor + { + /// + /// Creates a new instance of . + /// + /// A type that represents a . + public ModelValidatorProviderDescriptor([NotNull] Type type) + : base(type) + { + } + + /// + /// Creates a new instance of with the specified instance. + /// + /// An instance of . + public ModelValidatorProviderDescriptor([NotNull] IModelValidatorProvider validatorProvider) + : base(validatorProvider) + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptorExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptorExtensions.cs new file mode 100644 index 0000000000..f09874263a --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/OptionDescriptors/ModelValidatorProviderDescriptorExtensions.cs @@ -0,0 +1,94 @@ +// 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 Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.OptionDescriptors; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// Extension methods for adding validator provider to a collection. + /// + public static class ModelValidatorProviderDescriptorExtensions + { + /// + /// Adds a type representing a to . + /// + /// A list of . + /// Type representing an + /// A representing the added instance. + public static ModelValidatorProviderDescriptor Add( + [NotNull] this IList descriptors, + [NotNull] Type modelValidatorProviderType) + { + var descriptor = new ModelValidatorProviderDescriptor(modelValidatorProviderType); + descriptors.Add(descriptor); + return descriptor; + } + + /// + /// Inserts a type representing a in to at + /// the specified . + /// + /// A list of . + /// The zero-based index at which + /// should be inserted. + /// Type representing an + /// A representing the inserted instance. + public static ModelValidatorProviderDescriptor Insert( + [NotNull] this IList descriptors, + int index, + [NotNull] Type modelValidatorProviderType) + { + if (index < 0 || index > descriptors.Count) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + var descriptor = new ModelValidatorProviderDescriptor(modelValidatorProviderType); + descriptors.Insert(index, descriptor); + return descriptor; + } + + /// + /// Adds an to . + /// + /// A list of . + /// An instance. + /// A representing the added instance. + public static ModelValidatorProviderDescriptor Add( + [NotNull] this IList descriptors, + [NotNull] IModelValidatorProvider modelValidatorProvider) + { + var descriptor = new ModelValidatorProviderDescriptor(modelValidatorProvider); + descriptors.Add(descriptor); + return descriptor; + } + + /// + /// Insert an in to at the specified + /// . + /// + /// A list of . + /// The zero-based index at which + /// should be inserted. + /// An instance. + /// A representing the added instance. + public static ModelValidatorProviderDescriptor Insert( + [NotNull] this IList descriptors, + int index, + [NotNull] IModelValidatorProvider modelValidatorProvider) + { + if (index < 0 || index > descriptors.Count) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + var descriptor = new ModelValidatorProviderDescriptor(modelValidatorProvider); + descriptors.Insert(index, descriptor); + return descriptor; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs index cf5047179c..ebefea9629 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.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 Microsoft.AspNet.Mvc.ModelBinding; namespace Microsoft.AspNet.Mvc @@ -13,14 +12,14 @@ public ActionBindingContext(ActionContext context, IModelBinder modelBinder, IValueProvider valueProvider, IInputFormatterSelector inputFormatterSelector, - IEnumerable validatorProviders) + IModelValidatorProvider validatorProvider) { ActionContext = context; MetadataProvider = metadataProvider; ModelBinder = modelBinder; ValueProvider = valueProvider; InputFormatterSelector = inputFormatterSelector; - ValidatorProviders = validatorProviders; + ValidatorProvider = validatorProvider; } public ActionContext ActionContext { get; private set; } @@ -33,6 +32,6 @@ public ActionBindingContext(ActionContext context, public IInputFormatterSelector InputFormatterSelector { get; private set; } - public IEnumerable ValidatorProviders { get; private set; } + public IModelValidatorProvider ValidatorProvider { get; private set; } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs index dcadab07ee..2c80132742 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.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.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNet.Mvc.ModelBinding; @@ -14,20 +13,20 @@ public class DefaultActionBindingContextProvider : IActionBindingContextProvider private readonly ICompositeModelBinder _compositeModelBinder; private readonly IValueProviderFactory _compositeValueProviderFactory; private readonly IInputFormatterSelector _inputFormatterSelector; - private readonly IEnumerable _validatorProviders; + private readonly ICompositeModelValidatorProvider _validatorProvider; private Tuple _bindingContext; public DefaultActionBindingContextProvider(IModelMetadataProvider modelMetadataProvider, ICompositeModelBinder compositeModelBinder, ICompositeValueProviderFactory compositeValueProviderFactory, IInputFormatterSelector inputFormatterProvider, - IEnumerable validatorProviders) + ICompositeModelValidatorProvider validatorProvider) { _modelMetadataProvider = modelMetadataProvider; _compositeModelBinder = compositeModelBinder; _compositeValueProviderFactory = compositeValueProviderFactory; _inputFormatterSelector = inputFormatterProvider; - _validatorProviders = validatorProviders; + _validatorProvider = validatorProvider; } public Task GetActionBindingContextAsync(ActionContext actionContext) @@ -52,7 +51,7 @@ public Task GetActionBindingContextAsync(ActionContext act _compositeModelBinder, valueProvider, _inputFormatterSelector, - _validatorProviders); + _validatorProvider); _bindingContext = new Tuple(actionContext, context); diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ModelBindingHelper.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ModelBindingHelper.cs index 67846420d4..7ff39f60c1 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ModelBindingHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ModelBindingHelper.cs @@ -23,8 +23,8 @@ public static class ModelBindingHelper /// The provider used for reading metadata for the model type. /// The model binder used for binding. /// The value provider used for looking up values. - /// The validator providers used for executing validation - /// on the model instance. + /// The validator provider used for executing validation on the model + /// instance. /// A Task with a value representing if the the update is successful. public static async Task TryUpdateModelAsync( [NotNull] TModel model, @@ -34,7 +34,7 @@ public static async Task TryUpdateModelAsync( [NotNull] IModelMetadataProvider metadataProvider, [NotNull] IModelBinder modelBinder, [NotNull] IValueProvider valueProvider, - [NotNull] IEnumerable validatorProviders) + [NotNull] IModelValidatorProvider validatorProvider) where TModel : class { var modelMetadata = metadataProvider.GetMetadataForType( @@ -49,7 +49,7 @@ public static async Task TryUpdateModelAsync( ModelState = modelState, ModelBinder = modelBinder, ValueProvider = valueProvider, - ValidatorProviders = validatorProviders, + ValidatorProvider = validatorProvider, MetadataProvider = metadataProvider, FallbackToEmptyPrefix = true, HttpContext = httpContext diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs index c02a24a186..c8e610a5d9 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs @@ -1315,13 +1315,12 @@ public IEnumerable GetClientValidationRules( string name) { var actionBindingContext = _actionBindingContextProvider.GetActionBindingContextAsync(ViewContext).Result; - metadata = metadata ?? - ExpressionMetadataProvider.FromStringExpression(name, ViewData, MetadataProvider); - return actionBindingContext.ValidatorProviders - .SelectMany(vp => vp.GetValidators(metadata)) - .OfType() - .SelectMany(v => v.GetClientValidationRules( - new ClientModelValidationContext(metadata, MetadataProvider))); + metadata = metadata ?? ExpressionMetadataProvider.FromStringExpression(name, ViewData, MetadataProvider); + return actionBindingContext.ValidatorProvider + .GetValidators(metadata) + .OfType() + .SelectMany(v => v.GetClientValidationRules( + new ClientModelValidationContext(metadata, MetadataProvider))); } // Only need a dictionary if htmlAttributes is non-null. TagBuilder.MergeAttributes() is fine with null. diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs index e79b2e7e9b..0b63a73c7d 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs @@ -80,7 +80,7 @@ public virtual async Task BindModelAsync([NotNull] ModelBindingContext bin } var validationContext = new ModelValidationContext(bindingContext.MetadataProvider, - bindingContext.ValidatorProviders, + bindingContext.ValidatorProvider, bindingContext.ModelState, bindingContext.ModelMetadata, containerMetadata: null); @@ -120,7 +120,7 @@ private static ModelBindingContext CreateNewBindingContext(ModelBindingContext o ModelName = modelName, ModelState = oldBindingContext.ModelState, ValueProvider = oldBindingContext.ValueProvider, - ValidatorProviders = oldBindingContext.ValidatorProviders, + ValidatorProvider = oldBindingContext.ValidatorProvider, MetadataProvider = oldBindingContext.MetadataProvider, ModelBinder = oldBindingContext.ModelBinder, HttpContext = oldBindingContext.HttpContext diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/MutableObjectModelBinder.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/MutableObjectModelBinder.cs index c0ce93dc8a..98599dc5de 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/MutableObjectModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/MutableObjectModelBinder.cs @@ -170,8 +170,9 @@ internal static PropertyValidationInfo GetPropertyValidationInfo(ModelBindingCon { var propertyName = property.Name; var propertyMetadata = bindingContext.PropertyMetadata[propertyName]; - var requiredValidator = bindingContext.GetValidators(propertyMetadata) - .FirstOrDefault(v => v.IsRequired); + var requiredValidator = bindingContext.ValidatorProvider + .GetValidators(propertyMetadata) + .FirstOrDefault(v => v != null && v.IsRequired); if (requiredValidator != null) { validationInfo.RequiredValidators[propertyName] = requiredValidator; diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Internal/ModelBindingContextExtensions.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Internal/ModelBindingContextExtensions.cs deleted file mode 100644 index 6e4e7864eb..0000000000 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Internal/ModelBindingContextExtensions.cs +++ /dev/null @@ -1,26 +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.Internal -{ - internal static class ModelBindingContextExtensions - { - public static IEnumerable GetValidators([NotNull] this ModelBindingContext context, - [NotNull] ModelMetadata metadata) - { - return context.ValidatorProviders.SelectMany(vp => vp.GetValidators(metadata)) - .Where(v => v != null); - } - - public static IEnumerable Validate([NotNull] this ModelBindingContext bindingContext) - { - var validators = GetValidators(bindingContext, bindingContext.ModelMetadata); - var compositeValidator = new CompositeModelValidator(validators); - var modelValidationContext = new ModelValidationContext(bindingContext, bindingContext.ModelMetadata); - return compositeValidator.Validate(modelValidationContext); - } - } -} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/ModelBindingContext.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/ModelBindingContext.cs index a5870a7225..3424266c93 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/ModelBindingContext.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/ModelBindingContext.cs @@ -41,7 +41,7 @@ public ModelBindingContext(ModelBindingContext bindingContext) ValueProvider = bindingContext.ValueProvider; MetadataProvider = bindingContext.MetadataProvider; ModelBinder = bindingContext.ModelBinder; - ValidatorProviders = bindingContext.ValidatorProviders; + ValidatorProvider = bindingContext.ValidatorProvider; HttpContext = bindingContext.HttpContext; } } @@ -148,10 +148,10 @@ public Type ModelType public IModelMetadataProvider MetadataProvider { get; set; } /// - /// Gets or sets the sequence of instances used for model validation - /// with this context. + /// Gets or sets the instance used for model validation with this + /// context. /// - public IEnumerable ValidatorProviders { get; set; } + public IModelValidatorProvider ValidatorProvider { get; set; } /// /// Gets a dictionary of property name to instances for diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/CompositeModelValidatorProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/CompositeModelValidatorProvider.cs new file mode 100644 index 0000000000..74d4b9b853 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/CompositeModelValidatorProvider.cs @@ -0,0 +1,31 @@ +// 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 +{ + /// + /// Default implementation for . + /// + public class CompositeModelValidatorProvider : ICompositeModelValidatorProvider + { + /// + /// Initializes a new instance of . + /// + /// The instance used to get the list of + /// activated that this instance delegates to. + public CompositeModelValidatorProvider(IModelValidatorProviderProvider provider) + { + ValidatorProviders = provider.ModelValidatorProviders; + } + + public IReadOnlyList ValidatorProviders { get; private set; } + + public IEnumerable GetValidators(ModelMetadata metadata) + { + return ValidatorProviders.SelectMany(v => v.GetValidators(metadata)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ICompositeModelValidatorProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ICompositeModelValidatorProvider.cs new file mode 100644 index 0000000000..ad3a28d788 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ICompositeModelValidatorProvider.cs @@ -0,0 +1,12 @@ +// 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 +{ + /// + /// Represents an aggregate of that delegates to underlying providers. + /// + public interface ICompositeModelValidatorProvider : IModelValidatorProvider + { + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidatorProviderProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidatorProviderProvider.cs new file mode 100644 index 0000000000..5bac7b77e2 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/IModelValidatorProviderProvider.cs @@ -0,0 +1,18 @@ +// 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 +{ + /// + /// Provides an activated collection of instances. + /// + public interface IModelValidatorProviderProvider + { + /// + /// Gets a collection of activated instances. + /// + IReadOnlyList ModelValidatorProviders { get; } + } +} \ 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 a9e25ce263..ddda6462e5 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationContext.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationContext.cs @@ -1,32 +1,30 @@ // 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 { public class ModelValidationContext { public ModelValidationContext([NotNull] ModelBindingContext bindingContext, [NotNull] ModelMetadata metadata) - : this(bindingContext.MetadataProvider, - bindingContext.ValidatorProviders, - bindingContext.ModelState, - metadata, + : this(bindingContext.MetadataProvider, + bindingContext.ValidatorProvider, + bindingContext.ModelState, + metadata, bindingContext.ModelMetadata) { } - public ModelValidationContext([NotNull] IModelMetadataProvider metadataProvider, - [NotNull] IEnumerable validatorProviders, - [NotNull] ModelStateDictionary modelState, - [NotNull] ModelMetadata metadata, + public ModelValidationContext([NotNull] IModelMetadataProvider metadataProvider, + [NotNull] IModelValidatorProvider validatorProvider, + [NotNull] ModelStateDictionary modelState, + [NotNull] ModelMetadata metadata, ModelMetadata containerMetadata) { ModelMetadata = metadata; ModelState = modelState; MetadataProvider = metadataProvider; - ValidatorProviders = validatorProviders; + ValidatorProvider = validatorProvider; ContainerMetadata = containerMetadata; } @@ -37,7 +35,7 @@ public ModelValidationContext([NotNull] ModelValidationContext parentContext, ContainerMetadata = parentContext.ModelMetadata; ModelState = parentContext.ModelState; MetadataProvider = parentContext.MetadataProvider; - ValidatorProviders = parentContext.ValidatorProviders; + ValidatorProvider = parentContext.ValidatorProvider; } public ModelMetadata ModelMetadata { get; private set; } @@ -48,6 +46,6 @@ public ModelValidationContext([NotNull] ModelValidationContext parentContext, public IModelMetadataProvider MetadataProvider { get; private set; } - public IEnumerable ValidatorProviders { get; private set; } + public IModelValidatorProvider ValidatorProvider { get; private set; } } } diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationNode.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationNode.cs index 7faaed9cf6..f49c2dad23 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationNode.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ModelValidationNode.cs @@ -215,7 +215,7 @@ private void ValidateThis(ModelValidationContext validationContext, ModelValidat private static IEnumerable GetValidators(ModelValidationContext validationContext, ModelMetadata metadata) { - return validationContext.ValidatorProviders.SelectMany(vp => vp.GetValidators(metadata)); + return validationContext.ValidatorProvider.GetValidators(metadata); } } } diff --git a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs index 7d42cb0d39..e52b291438 100644 --- a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs +++ b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs @@ -1,9 +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.Collections.Generic; using Microsoft.AspNet.Mvc.ModelBinding; -using Microsoft.AspNet.Mvc.OptionDescriptors; using Microsoft.AspNet.Mvc.Razor; using Microsoft.Framework.OptionsModel; @@ -54,6 +52,10 @@ public void Setup(MvcOptions options) options.ValueProviderFactories.Add(new RouteValueValueProviderFactory()); options.ValueProviderFactories.Add(new QueryStringValueProviderFactory()); options.ValueProviderFactories.Add(new FormValueProviderFactory()); + + // Set up validators + options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider()); + options.ModelValidatorProviders.Add(new DataMemberModelValidatorProvider()); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index d91082535e..5458c69779 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -81,8 +81,8 @@ public static IEnumerable GetDefaultServices(IConfiguration yield return describe.Transient, DefaultFilterProvider>(); - yield return describe.Transient(); - yield return describe.Transient(); + yield return describe.Transient(); + yield return describe.Scoped(); yield return describe.Scoped(); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs index 960445641a..83416e1dcb 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs @@ -578,7 +578,7 @@ public async Task TryUpdateModel_UsesModelTypeNameIfNotSpecified() binder.Object, valueProvider, Mock.Of(), - Enumerable.Empty()); + Mock.Of()); var bindingContextProvider = new Mock(); bindingContextProvider.Setup(b => b.GetActionBindingContextAsync(actionContext)) .Returns(Task.FromResult(bindingContext)); @@ -619,7 +619,7 @@ public async Task TryUpdateModel_UsesModelTypeNameIfSpecified() binder.Object, valueProvider, Mock.Of(), - Enumerable.Empty()); + Mock.Of()); var bindingContextProvider = new Mock(); bindingContextProvider.Setup(b => b.GetActionBindingContextAsync(actionContext)) .Returns(Task.FromResult(bindingContext)); @@ -660,7 +660,7 @@ public async Task TryUpdateModel_UsesModelValueProviderIfSpecified() binder.Object, Mock.Of(), Mock.Of(), - Enumerable.Empty()); + Mock.Of()); var bindingContextProvider = new Mock(); bindingContextProvider.Setup(b => b.GetActionBindingContextAsync(actionContext)) .Returns(Task.FromResult(bindingContext)); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/DefaultValidationProviderProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/DefaultValidationProviderProviderTest.cs new file mode 100644 index 0000000000..5b291a19a4 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/DefaultValidationProviderProviderTest.cs @@ -0,0 +1,66 @@ +// 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 Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.OptionsModel; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.OptionDescriptors +{ + public class DefaultValidationProviderProviderTest + { + [Fact] + public void ValidationProviders_ReturnsInstantiatedListOfValueProviders() + { + // Arrange + var service = Mock.Of(); + var validationProvider = Mock.Of(); + var type = typeof(TestModelValidationProvider); + var typeActivator = new TypeActivator(); + var serviceProvider = new Mock(); + serviceProvider.Setup(p => p.GetService(typeof(ITestService))) + .Returns(service); + var options = new MvcOptions(); + options.ModelValidatorProviders.Add(type); + options.ModelValidatorProviders.Add(validationProvider); + var accessor = new Mock>(); + accessor.SetupGet(a => a.Options) + .Returns(options); + var provider = new DefaultModelValidatorProviderProvider(accessor.Object, + typeActivator, + serviceProvider.Object); + + // Act + var result = provider.ModelValidatorProviders; + + // Assert + Assert.Equal(2, result.Count); + var testModelValidationProvider = Assert.IsType(result[0]); + Assert.Same(service, testModelValidationProvider.Service); + Assert.Same(validationProvider, result[1]); + } + + private class TestModelValidationProvider : IModelValidatorProvider + { + public TestModelValidationProvider(ITestService service) + { + Service = service; + } + + public ITestService Service { get; private set; } + + public IEnumerable GetValidators(ModelMetadata metadata) + { + throw new NotImplementedException(); + } + } + + public interface ITestService + { + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptor.cs b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptor.cs new file mode 100644 index 0000000000..34c583dd3a --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptor.cs @@ -0,0 +1,63 @@ +// 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 Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Testing; +using Xunit; + +namespace Microsoft.AspNet.Mvc.OptionDescriptors +{ + public class ModelValidatorProviderDescriptorTest + { + [Fact] + public void ConstructorThrows_IfTypeIsNotModelValidatorProvider() + { + // Arrange + var validatorProviderType = typeof(IModelValidatorProvider).FullName; + var type = typeof(string); + var expected = string.Format("The type '{0}' must derive from '{1}'.", + type.FullName, validatorProviderType); + + // Act & Assert + ExceptionAssert.ThrowsArgument(() => new ModelValidatorProviderDescriptor(type), "type", expected); + } + + [Fact] + public void ConstructorSetsModelValidatorProviderType() + { + // Arrange + var type = typeof(TestModelValidatorProvider); + + // Act + var descriptor = new ModelValidatorProviderDescriptor(type); + + // Assert + Assert.Equal(type, descriptor.OptionType); + Assert.Null(descriptor.Instance); + } + + [Fact] + public void ConstructorSetsInstanceAndType() + { + // Arrange + var validatorProvider = new TestModelValidatorProvider(); + + // Act + var descriptor = new ModelValidatorProviderDescriptor(validatorProvider); + + // Assert + Assert.Same(validatorProvider, descriptor.Instance); + Assert.Equal(validatorProvider.GetType(), descriptor.OptionType); + } + + private class TestModelValidatorProvider : IModelValidatorProvider + { + public IEnumerable GetValidators(ModelMetadata metadata) + { + throw new NotImplementedException(); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptorExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptorExtensionsTest.cs new file mode 100644 index 0000000000..c89752ff33 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/ModelValidatorProviderDescriptorExtensionsTest.cs @@ -0,0 +1,73 @@ +// 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 Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.OptionDescriptors; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc +{ + public class ModelValidatorProviderDescriptorExtensionsTest + { + [Theory] + [InlineData(-1)] + [InlineData(5)] + public void Insert_WithType_ThrowsIfIndexIsOutOfBounds(int index) + { + // Arrange + var collection = new List + { + new ModelValidatorProviderDescriptor(Mock.Of()), + new ModelValidatorProviderDescriptor(Mock.Of()) + }; + + // Act & Assert + Assert.Throws("index", + () => collection.Insert(index, typeof(IModelValidatorProvider))); + } + + [Theory] + [InlineData(-2)] + [InlineData(3)] + public void Insert_WithInstance_ThrowsIfIndexIsOutOfBounds(int index) + { + // Arrange + var collection = new List + { + new ModelValidatorProviderDescriptor(Mock.Of()), + new ModelValidatorProviderDescriptor(Mock.Of()) + }; + var valueProviderFactory = Mock.Of(); + + // Act & Assert + Assert.Throws("index", () => collection.Insert(index, valueProviderFactory)); + } + + [InlineData] + public void ModelValidatorProviderDescriptors_AddsTypesAndInstances() + { + // Arrange + var provider1 = Mock.Of(); + var provider2 = Mock.Of(); + var type1 = typeof(DataMemberModelValidatorProvider); + var type2 = typeof(DataAnnotationsModelValidatorProvider); + var collection = new List(); + + // Act + collection.Add(provider2); + collection.Insert(0, provider1); + collection.Add(type2); + collection.Insert(2, type1); + + // Assert + 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[3].OptionType); + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ModelBindingHelperTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ModelBindingHelperTest.cs index b5c4815582..4dab8b4934 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ModelBindingHelperTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/ModelBindingHelperTest.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Mvc.ModelBinding; @@ -39,7 +38,7 @@ public async Task TryUpdateModel_ReturnsFalse_IfBinderReturnsFalse() metadataProvider.Object, GetCompositeBinder(binder.Object), Mock.Of(), - Enumerable.Empty()); + Mock.Of()); // Assert Assert.False(result); @@ -76,7 +75,7 @@ public async Task TryUpdateModel_ReturnsFalse_IfModelValidationFails() new DataAnnotationsModelMetadataProvider(), GetCompositeBinder(binders), valueProvider, - new[] { validator }); + validator); // Assert Assert.False(result); @@ -114,7 +113,7 @@ public async Task TryUpdateModel_ReturnsTrue_IfModelBindsAndValidatesSuccessfull new DataAnnotationsModelMetadataProvider(), GetCompositeBinder(binders), valueProvider, - new[] { validator }); + validator); // Assert Assert.True(result); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ReflectedActionInvokerTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ReflectedActionInvokerTest.cs index dde7523440..514e5b7103 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ReflectedActionInvokerTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ReflectedActionInvokerTest.cs @@ -1370,7 +1370,7 @@ public async Task GetActionArguments_DoesNotAddActionArgumentsToModelStateDictio binder.Object, Mock.Of(), Mock.Of(), - Enumerable.Empty()); + Mock.Of()); var actionBindingContextProvider = new Mock(); actionBindingContextProvider.Setup(p => p.GetActionBindingContextAsync(It.IsAny())) @@ -1429,7 +1429,7 @@ public async Task GetActionArguments_AddsActionArgumentsToModelStateDictionary_I binder.Object, Mock.Of(), Mock.Of(), - Enumerable.Empty()); + Mock.Of()); var actionBindingContextProvider = new Mock(); actionBindingContextProvider.Setup(p => p.GetActionBindingContextAsync(It.IsAny())) @@ -1489,7 +1489,7 @@ public async Task Invoke_UsesDefaultValuesIfNotBound() binder.Object, Mock.Of(), Mock.Of(), - Enumerable.Empty()); + Mock.Of()); var actionBindingContextProvider = new Mock(); actionBindingContextProvider.Setup(p => p.GetActionBindingContextAsync(It.IsAny())) diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs index 937ff4d7a0..c995d88788 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs @@ -93,7 +93,7 @@ public static HtmlHelper GetHtmlHelper( Mock.Of(), Mock.Of(), Mock.Of(), - Enumerable.Empty()); + Mock.Of()); var urlHelper = Mock.Of(); var actionBindingContextProvider = new Mock(); actionBindingContextProvider diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs index 91a0bc4449..12c7391853 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs @@ -31,7 +31,7 @@ public async Task BindModel_SuccessfulBind_RunsValidationAndReturnsModel() { { "someName", "dummyValue" } }, - ValidatorProviders = Enumerable.Empty() + ValidatorProvider = GetValidatorProvider() }; var mockIntBinder = new Mock(); @@ -78,14 +78,14 @@ public async Task BindModel_SuccessfulBind_ComplexTypeFallback_RunsValidationAnd { { "someOtherName", "dummyValue" } }, - ValidatorProviders = Enumerable.Empty() + ValidatorProvider = GetValidatorProvider() }; var mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModelAsync(It.IsAny())) .Returns( - delegate(ModelBindingContext mbc) + delegate (ModelBindingContext mbc) { if (!string.IsNullOrEmpty(mbc.ModelName)) { @@ -122,7 +122,7 @@ public async Task BindModel_UnsuccessfulBind_BinderFails_ReturnsNull() .Returns(Task.FromResult(false)) .Verifiable(); - var shimBinder = (IModelBinder)mockListBinder.Object; + var shimBinder = mockListBinder.Object; var bindingContext = new ModelBindingContext { @@ -233,7 +233,7 @@ public async Task BindModel_WithDefaultValidators_ValidatesSubProperties() { "user.password", "password-val" }, { "user.confirmpassword", "not-password-val" }, }; - var bindingContext = CreateBindingContext(binder, valueProvider, typeof(User), new[] { validatorProvider }); + var bindingContext = CreateBindingContext(binder, valueProvider, typeof(User), validatorProvider); bindingContext.ModelName = "user"; // Act @@ -255,7 +255,7 @@ public async Task BindModel_WithDefaultValidators_ValidatesInstance() { "user.password", "password" }, { "user.confirmpassword", "password" }, }; - var bindingContext = CreateBindingContext(binder, valueProvider, typeof(User), new[] { validatorProvider }); + var bindingContext = CreateBindingContext(binder, valueProvider, typeof(User), validatorProvider); bindingContext.ModelName = "user"; // Act @@ -269,9 +269,9 @@ public async Task BindModel_WithDefaultValidators_ValidatesInstance() private static ModelBindingContext CreateBindingContext(IModelBinder binder, IValueProvider valueProvider, Type type, - IEnumerable validatorProviders = null) + IModelValidatorProvider validatorProvider = null) { - validatorProviders = validatorProviders ?? Enumerable.Empty(); + validatorProvider = validatorProvider ?? GetValidatorProvider(); var metadataProvider = new DataAnnotationsModelMetadataProvider(); var bindingContext = new ModelBindingContext { @@ -281,7 +281,7 @@ private static ModelBindingContext CreateBindingContext(IModelBinder binder, ModelMetadata = metadataProvider.GetMetadataForType(null, type), ModelState = new ModelStateDictionary(), ValueProvider = valueProvider, - ValidatorProviders = validatorProviders + ValidatorProvider = validatorProvider }; return bindingContext; } @@ -293,8 +293,8 @@ private static CompositeModelBinder CreateBinderWithDefaults() typeActivator .Setup(t => t.CreateInstance(serviceProvider, It.IsAny(), It.IsAny())) .Returns((IServiceProvider sp, Type t, object[] args) => Activator.CreateInstance(t)); - var binders = new IModelBinder[] - { + var binders = new IModelBinder[] + { new TypeMatchModelBinder(), new ByteArrayModelBinder(), new GenericModelBinder(serviceProvider, typeActivator.Object), @@ -319,6 +319,15 @@ private static CompositeModelBinder CreateCompositeBinder(IModelBinder mockIntBi return shimBinder; } + private static IModelValidatorProvider GetValidatorProvider(params IModelValidator[] validators) + { + var provider = new Mock(); + provider.Setup(v => v.GetValidators(It.IsAny())) + .Returns(validators ?? Enumerable.Empty()); + + return provider.Object; + } + private class SimplePropertiesModel { public string FirstName { get; set; } diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs index ec1f9386a3..bac2f61e38 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs @@ -122,7 +122,7 @@ private static ModelBindingContext GetBindingContext(IValueProvider valueProvide ValueProvider = valueProvider, ModelBinder = innerBinder ?? CreateIntBinder(), MetadataProvider = metataProvider, - ValidatorProviders = Enumerable.Empty() + ValidatorProvider = Mock.Of() }; return bindingContext; } diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/MutableObjectModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/MutableObjectModelBinderTest.cs index 31c15a193d..ecc84b9fa8 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/MutableObjectModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/MutableObjectModelBinderTest.cs @@ -32,7 +32,7 @@ public async Task BindModel_InitsInstance() ValueProvider = mockValueProvider.Object, ModelBinder = mockDtoBinder.Object, MetadataProvider = new DataAnnotationsModelMetadataProvider(), - ValidatorProviders = Enumerable.Empty() + ValidatorProvider = Mock.Of() }; mockDtoBinder @@ -194,7 +194,7 @@ public void GetMetadataForProperties_WithBindAttribute() var bindingContext = new ModelBindingContext { ModelMetadata = GetMetadataForType(typeof(PersonWithBindExclusion)), - ValidatorProviders = Enumerable.Empty() + ValidatorProvider = Mock.Of() }; var testableBinder = new TestableMutableObjectModelBinder(); @@ -215,7 +215,7 @@ public void GetMetadataForProperties_WithoutBindAttribute() var bindingContext = new ModelBindingContext { ModelMetadata = GetMetadataForType(typeof(Person)), - ValidatorProviders = Enumerable.Empty() + ValidatorProvider = Mock.Of() }; var testableBinder = new TestableMutableObjectModelBinder(); @@ -235,7 +235,7 @@ public void GetRequiredPropertiesCollection_MixedAttributes() var bindingContext = new ModelBindingContext { ModelMetadata = GetMetadataForObject(new ModelWithMixedBindingBehaviors()), - ValidatorProviders = Enumerable.Empty() + ValidatorProvider = Mock.Of() }; // Act @@ -256,7 +256,7 @@ public void NullCheckFailedHandler_ModelStateAlreadyInvalid_DoesNothing() var modelMetadata = GetMetadataForType(typeof(Person)); var validationNode = new ModelValidationNode(modelMetadata, "foo"); var validationContext = new ModelValidationContext(new DataAnnotationsModelMetadataProvider(), - Enumerable.Empty(), + Mock.Of(), modelState, modelMetadata, null); @@ -278,7 +278,7 @@ public void NullCheckFailedHandler_ModelStateValid_AddsErrorString() var modelMetadata = GetMetadataForType(typeof(Person)); var validationNode = new ModelValidationNode(modelMetadata, "foo"); var validationContext = new ModelValidationContext(new DataAnnotationsModelMetadataProvider(), - Enumerable.Empty(), + Mock.Of(), modelState, modelMetadata, null); @@ -309,7 +309,7 @@ public void ProcessDto_BindRequiredFieldMissing_RaisesModelError() { ModelMetadata = containerMetadata, ModelName = "theModel", - ValidatorProviders = Enumerable.Empty() + ValidatorProvider = Mock.Of() }; var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties); @@ -354,10 +354,10 @@ public void ProcessDto_BindRequiredFieldNull_RaisesModelError() ModelMetadata = containerMetadata, ModelName = "theModel", ModelState = new ModelStateDictionary(), - ValidatorProviders = Enumerable.Empty() + ValidatorProvider = Mock.Of() }; var validationContext = new ModelValidationContext(new EmptyModelMetadataProvider(), - bindingContext.ValidatorProviders, + bindingContext.ValidatorProvider, bindingContext.ModelState, containerMetadata, null); @@ -590,10 +590,9 @@ public void SetProperty_PropertyHasDefaultValue_SetsDefaultValue() var propertyMetadata = bindingContext.ModelMetadata.Properties.First(o => o.PropertyName == "PropertyWithDefaultValue"); var validationNode = new ModelValidationNode(propertyMetadata, "foo"); var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode); - var requiredValidator = bindingContext.ValidatorProviders - .SelectMany(v => v.GetValidators(propertyMetadata)) - .Where(v => v.IsRequired) - .FirstOrDefault(); + var requiredValidator = bindingContext.ValidatorProvider + .GetValidators(propertyMetadata) + .FirstOrDefault(v => v.IsRequired); var testableBinder = new TestableMutableObjectModelBinder(); @@ -634,10 +633,9 @@ public void SetProperty_PropertyIsSettable_CallsSetter() var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth"); var validationNode = new ModelValidationNode(propertyMetadata, "foo"); var dtoResult = new ComplexModelDtoResult(new DateTime(2001, 1, 1), validationNode); - var requiredValidator = bindingContext.ValidatorProviders - .SelectMany(v => v.GetValidators(propertyMetadata)) - .Where(v => v.IsRequired) - .FirstOrDefault(); + var requiredValidator = bindingContext.ValidatorProvider + .GetValidators(propertyMetadata) + .FirstOrDefault(v => v.IsRequired); var validationContext = new ModelValidationContext(bindingContext, propertyMetadata); var testableBinder = new TestableMutableObjectModelBinder(); @@ -769,42 +767,45 @@ public void SetProperty_SettingNullableTypeToNull_RequiredValidatorPresent_Prope private static ModelBindingContext CreateContext(ModelMetadata metadata) { + var provider = new Mock(); + provider.SetupGet(p => p.ModelValidatorProviders) + .Returns(new IModelValidatorProvider[] + { + new DataAnnotationsModelValidatorProvider(), + new DataMemberModelValidatorProvider() + }); + return new ModelBindingContext { ModelState = new ModelStateDictionary(), ModelMetadata = metadata, ModelName = "theModel", - ValidatorProviders = new IModelValidatorProvider[] - { - new DataAnnotationsModelValidatorProvider(), - new DataMemberModelValidatorProvider() - } + ValidatorProvider = new CompositeModelValidatorProvider(provider.Object) }; } private static IModelValidator GetRequiredValidator(ModelBindingContext bindingContext, ModelMetadata propertyMetadata) { - return bindingContext.ValidatorProviders - .SelectMany(v => v.GetValidators(propertyMetadata)) - .Where(v => v.IsRequired) - .FirstOrDefault(); + return bindingContext.ValidatorProvider + .GetValidators(propertyMetadata) + .FirstOrDefault(v => v.IsRequired); } private static ModelMetadata GetMetadataForCanUpdateProperty(string propertyName) { - DataAnnotationsModelMetadataProvider metadataProvider = new DataAnnotationsModelMetadataProvider(); + var metadataProvider = new DataAnnotationsModelMetadataProvider(); return metadataProvider.GetMetadataForProperty(null, typeof(MyModelTestingCanUpdateProperty), propertyName); } private static ModelMetadata GetMetadataForObject(object o) { - DataAnnotationsModelMetadataProvider metadataProvider = new DataAnnotationsModelMetadataProvider(); + var metadataProvider = new DataAnnotationsModelMetadataProvider(); return metadataProvider.GetMetadataForType(() => o, o.GetType()); } private static ModelMetadata GetMetadataForType(Type t) { - DataAnnotationsModelMetadataProvider metadataProvider = new DataAnnotationsModelMetadataProvider(); + var metadataProvider = new DataAnnotationsModelMetadataProvider(); return metadataProvider.GetMetadataForType(null, t); } diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompositeModelValidatorProviderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompositeModelValidatorProviderTest.cs new file mode 100644 index 0000000000..3987c8496f --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompositeModelValidatorProviderTest.cs @@ -0,0 +1,41 @@ +// 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 NET45 +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.ModelBinding.Test +{ + public class CompositeModelValidatorProviderTest + { + [Fact] + public void GetModelValidators_ReturnsValidatorsFromAllProviders() + { + // Arrange + 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 }); + var provider2 = new Mock(); + provider2.Setup(p => p.GetValidators(It.IsAny())) + .Returns(new[] { validator3 }); + var providerProvider = new Mock(); + providerProvider.Setup(p => p.ModelValidatorProviders) + .Returns(new[] { provider1.Object, provider2.Object }); + var compositeModelValidator = new CompositeModelValidatorProvider(providerProvider.Object); + var modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType( + modelAccessor: null, + modelType: typeof(string)); + + // Act + var result = compositeModelValidator.GetValidators(modelMetadata); + + // Assert + Assert.Equal(new[] { validator1, validator2, validator3 }, result); + } + } +} +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/ModelValidationNodeTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/ModelValidationNodeTest.cs index af92736ac0..4bf1fdeb3c 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/ModelValidationNodeTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/ModelValidationNodeTest.cs @@ -1,10 +1,12 @@ // 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 NET45 using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using Microsoft.AspNet.Testing; +using Moq; using Xunit; namespace Microsoft.AspNet.Mvc.ModelBinding @@ -294,8 +296,12 @@ private static ModelValidationContext CreateContext(ModelMetadata metadata = nul new DataMemberModelValidatorProvider() }; + var provider = new Mock(); + provider.SetupGet(p => p.ModelValidatorProviders) + .Returns(providers); + return new ModelValidationContext(new EmptyModelMetadataProvider(), - providers, + new CompositeModelValidatorProvider(provider.Object), new ModelStateDictionary(), metadata, null); @@ -343,4 +349,5 @@ private class ValidateAllPropertiesModel public string ValidString { get; set; } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs index ab6027e51d..062ccc50b4 100644 --- a/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs +++ b/test/Microsoft.AspNet.Mvc.Test/MvcOptionSetupTest.cs @@ -97,5 +97,21 @@ public void Setup_SetsUpInputFormatters() Assert.IsType(mvcOptions.InputFormatters[1].Instance); Assert.IsType(mvcOptions.InputFormatters[2].Instance); } + + [Fact] + public void Setup_SetsUpModelValidatorProviders() + { + // Arrange + var mvcOptions = new MvcOptions(); + var setup = new MvcOptionsSetup(); + + // Act + setup.Setup(mvcOptions); + + // Assert + Assert.Equal(2, mvcOptions.ModelValidatorProviders.Count); + Assert.IsType(mvcOptions.ModelValidatorProviders[0].Instance); + Assert.IsType(mvcOptions.ModelValidatorProviders[1].Instance); + } } } \ No newline at end of file