diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ArrayModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ArrayModelBinder.cs index 2d68d0e5a2..f7df7ae430 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ArrayModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ArrayModelBinder.cs @@ -25,6 +25,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding return base.BindModelAsync(bindingContext); } + protected override object CreateEmptyCollection() + { + return new TElement[0]; + } + /// protected override object GetModel(IEnumerable newCollection) { diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CollectionModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CollectionModelBinder.cs index 040f389dd4..fe4ffca3ac 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CollectionModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CollectionModelBinder.cs @@ -23,8 +23,31 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { ModelBindingHelper.ValidateBindingContext(bindingContext); + object model; + if (!await bindingContext.ValueProvider.ContainsPrefixAsync(bindingContext.ModelName)) { + // If this is the fallback case, and we failed to find data as a top-level model, then generate a + // default 'empty' model and return it. + var isTopLevelObject = bindingContext.ModelMetadata.ContainerType == null; + var hasExplicitAlias = bindingContext.BinderModelName != null; + + if (isTopLevelObject && (hasExplicitAlias || bindingContext.ModelName == string.Empty)) + { + model = CreateEmptyCollection(); + + var validationNode = new ModelValidationNode( + bindingContext.ModelName, + bindingContext.ModelMetadata, + model); + + return new ModelBindingResult( + model, + bindingContext.ModelName, + isModelSet: true, + validationNode: validationNode); + } + return null; } @@ -46,7 +69,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding boundCollection = result.Model; } - var model = bindingContext.Model; + model = bindingContext.Model; if (model == null) { model = GetModel(boundCollection); @@ -64,6 +87,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding validationNode: result?.ValidationNode); } + // Called when we're creating a default 'empty' model for a top level bind. + protected virtual object CreateEmptyCollection() + { + return new List(); + } + // Used when the ValueProvider contains the collection to be bound as a single element, e.g. the raw value // is [ "1", "2" ] and needs to be converted to an int[]. internal async Task BindSimpleCollection( @@ -165,8 +194,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding var didBind = false; object boundValue = null; - var modelType = bindingContext.ModelType; - var result = await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(childBindingContext); if (result != null && result.IsModelSet) diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryModelBinder.cs index f8bafd1532..24384b1ec2 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryModelBinder.cs @@ -18,5 +18,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { return newCollection?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); } + + protected override object CreateEmptyCollection() + { + return new Dictionary(); + } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs index 12e285e6b4..62ebdf047f 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs @@ -62,8 +62,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding } else { - // Caller (GenericModelBinder) was able to resolve a binder type and will create a ModelBindingResult - // that exits current ModelBinding loop. + // If this is the fallback case, and we failed to find data as a top-level model, then generate a + // default 'empty' model and return it. + var isTopLevelObject = bindingContext.ModelMetadata.ContainerType == null; + var hasExplicitAlias = bindingContext.BinderModelName != null; + + if (isTopLevelObject && (hasExplicitAlias || bindingContext.ModelName == string.Empty)) + { + var model = new KeyValuePair(); + + var validationNode = new ModelValidationNode( + bindingContext.ModelName, + bindingContext.ModelMetadata, + model); + + return new ModelBindingResult( + model, + bindingContext.ModelName, + isModelSet: true, + validationNode: validationNode); + } + return null; } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ArrayModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ArrayModelBinderTest.cs index 5161ec3893..86c7752e51 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ArrayModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ArrayModelBinderTest.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if DNX451 +using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.AspNet.Http.Internal; using Moq; using Xunit; @@ -35,14 +37,105 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test } [Fact] - public async Task BindModelAsync_ValueProviderDoesNotContainPrefix_ReturnsNull() + public async Task ArrayModelBinder_DoesNotCreateCollection_ForTopLevelModel_OnFirstPass() { // Arrange - var bindingContext = GetBindingContext(new SimpleHttpValueProvider()); - var binder = new ArrayModelBinder(); + var binder = new ArrayModelBinder(); + + var context = CreateContext(); + context.ModelName = "param"; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(string[])); + + context.ValueProvider = new TestValueProvider(new Dictionary()); // Act - var result = await binder.BindModelAsync(bindingContext); + var result = await binder.BindModelAsync(context); + + // Assert + Assert.Null(result); + } + + [Fact] + public async Task ArrayModelBinder_CreatesEmptyCollection_ForTopLevelModel_OnFallback() + { + // Arrange + var binder = new ArrayModelBinder(); + + var context = CreateContext(); + context.ModelName = string.Empty; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(string[])); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.NotNull(result); + + Assert.Empty(Assert.IsType(result.Model)); + Assert.Equal(string.Empty, result.Key); + Assert.True(result.IsModelSet); + + Assert.Same(result.ValidationNode.Model, result.Model); + Assert.Same(result.ValidationNode.Key, result.Key); + Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata); + } + + [Fact] + public async Task ArrayModelBinder_CreatesEmptyCollection_ForTopLevelModel_WithExplicitPrefix() + { + // Arrange + var binder = new ArrayModelBinder(); + + var context = CreateContext(); + context.ModelName = "prefix"; + context.BinderModelName = "prefix"; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(string[])); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.NotNull(result); + + Assert.Empty(Assert.IsType(result.Model)); + Assert.Equal("prefix", result.Key); + Assert.True(result.IsModelSet); + + Assert.Same(result.ValidationNode.Model, result.Model); + Assert.Same(result.ValidationNode.Key, result.Key); + Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata); + } + + [Theory] + [InlineData("")] + [InlineData("param")] + public async Task ArrayModelBinder_DoesNotCreateCollection_ForNonTopLevelModel(string prefix) + { + // Arrange + var binder = new ArrayModelBinder(); + + var context = CreateContext(); + context.ModelName = ModelNames.CreatePropertyModelName(prefix, "ArrayProperty"); + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForProperty( + typeof(ModelWithArrayProperty), + nameof(ModelWithArrayProperty.ArrayProperty)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); // Assert Assert.Null(result); @@ -155,6 +248,25 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test }; return bindingContext; } + + private static ModelBindingContext CreateContext() + { + var modelBindingContext = new ModelBindingContext() + { + OperationBindingContext = new OperationBindingContext() + { + HttpContext = new DefaultHttpContext(), + MetadataProvider = new TestModelMetadataProvider(), + } + }; + + return modelBindingContext; + } + + private class ModelWithArrayProperty + { + public string[] ArrayProperty { get; set; } + } } } #endif diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CollectionModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CollectionModelBinderTest.cs index c0f4af2331..dbde65ee76 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CollectionModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CollectionModelBinderTest.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Linq; #endif using System.Threading.Tasks; +using Microsoft.AspNet.Http.Internal; #if DNX451 using Moq; #endif @@ -216,6 +217,111 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test Assert.Null(boundCollection); } + [Fact] + public async Task CollectionModelBinder_DoesNotCreateCollection_ForTopLevelModel_OnFirstPass() + { + // Arrange + var binder = new CollectionModelBinder(); + + var context = CreateContext(); + context.ModelName = "param"; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(List)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.Null(result); + } + + [Fact] + public async Task CollectionModelBinder_CreatesEmptyCollection_ForTopLevelModel_OnFallback() + { + // Arrange + var binder = new CollectionModelBinder(); + + var context = CreateContext(); + context.ModelName = string.Empty; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(List)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.NotNull(result); + + Assert.Empty(Assert.IsType>(result.Model)); + Assert.Equal(string.Empty, result.Key); + Assert.True(result.IsModelSet); + + Assert.Same(result.ValidationNode.Model, result.Model); + Assert.Same(result.ValidationNode.Key, result.Key); + Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata); + } + + [Fact] + public async Task CollectionModelBinder_CreatesEmptyCollection_ForTopLevelModel_WithExplicitPrefix() + { + // Arrange + var binder = new CollectionModelBinder(); + + var context = CreateContext(); + context.ModelName = "prefix"; + context.BinderModelName = "prefix"; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(List)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.NotNull(result); + + Assert.Empty(Assert.IsType>(result.Model)); + Assert.Equal("prefix", result.Key); + Assert.True(result.IsModelSet); + + Assert.Same(result.ValidationNode.Model, result.Model); + Assert.Same(result.ValidationNode.Key, result.Key); + Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata); + } + + [Theory] + [InlineData("")] + [InlineData("param")] + public async Task CollectionModelBinder_DoesNotCreateCollection_ForNonTopLevelModel(string prefix) + { + // Arrange + var binder = new CollectionModelBinder(); + + var context = CreateContext(); + context.ModelName = ModelNames.CreatePropertyModelName(prefix, "ListProperty"); + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForProperty( + typeof(ModelWithListProperty), + nameof(ModelWithListProperty.ListProperty)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.Null(result); + } + #if DNX451 [Fact] public async Task BindSimpleCollection_SubBindingSucceeds() @@ -284,5 +390,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test return mockIntBinder.Object; } #endif + + private static ModelBindingContext CreateContext() + { + var modelBindingContext = new ModelBindingContext() + { + OperationBindingContext = new OperationBindingContext() + { + HttpContext = new DefaultHttpContext(), + MetadataProvider = new TestModelMetadataProvider(), + } + }; + + return modelBindingContext; + } + + private class ModelWithListProperty + { + public List ListProperty { get; set; } + } } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs index 34b0318aea..4ad8d02f32 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs @@ -4,6 +4,7 @@ #if DNX451 using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.AspNet.Http.Internal; using Moq; using Xunit; @@ -63,6 +64,125 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test Assert.Equal("eighty-four", dictionary[84]); } + [Fact] + public async Task DictionaryModelBinder_DoesNotCreateCollection_ForTopLevelModel_OnFirstPass() + { + // Arrange + var binder = new DictionaryModelBinder(); + + var context = CreateContext(); + context.ModelName = "param"; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(Dictionary)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.Null(result); + } + + [Fact] + public async Task DictionaryModelBinder_CreatesEmptyCollection_ForTopLevelModel_OnFallback() + { + // Arrange + var binder = new DictionaryModelBinder(); + + var context = CreateContext(); + context.ModelName = string.Empty; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(Dictionary)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.NotNull(result); + + Assert.Empty(Assert.IsType>(result.Model)); + Assert.Equal(string.Empty, result.Key); + Assert.True(result.IsModelSet); + + Assert.Same(result.ValidationNode.Model, result.Model); + Assert.Same(result.ValidationNode.Key, result.Key); + Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata); + } + + [Fact] + public async Task DictionaryModelBinder_CreatesEmptyCollection_ForTopLevelModel_WithExplicitPrefix() + { + // Arrange + var binder = new DictionaryModelBinder(); + + var context = CreateContext(); + context.ModelName = "prefix"; + context.BinderModelName = "prefix"; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(Dictionary)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.NotNull(result); + + Assert.Empty(Assert.IsType>(result.Model)); + Assert.Equal("prefix", result.Key); + Assert.True(result.IsModelSet); + + Assert.Same(result.ValidationNode.Model, result.Model); + Assert.Same(result.ValidationNode.Key, result.Key); + Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata); + } + + [Theory] + [InlineData("")] + [InlineData("param")] + public async Task DictionaryModelBinder_DoesNotCreateCollection_ForNonTopLevelModel(string prefix) + { + // Arrange + var binder = new DictionaryModelBinder(); + + var context = CreateContext(); + context.ModelName = ModelNames.CreatePropertyModelName(prefix, "ListProperty"); + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForProperty( + typeof(ModelWithDictionaryProperty), + nameof(ModelWithDictionaryProperty.DictionaryProperty)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.Null(result); + } + + private static ModelBindingContext CreateContext() + { + var modelBindingContext = new ModelBindingContext() + { + OperationBindingContext = new OperationBindingContext() + { + HttpContext = new DefaultHttpContext(), + MetadataProvider = new TestModelMetadataProvider(), + } + }; + + return modelBindingContext; + } + private static ModelBindingContext GetModelBindingContext(bool isReadOnly) { var metadataProvider = new TestModelMetadataProvider(); @@ -105,6 +225,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test }); return mockKvpBinder.Object; } + + private class ModelWithDictionaryProperty + { + public Dictionary DictionaryProperty { get; set; } + } } } #endif diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs index 1b9b463e3e..257eda9b21 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Moq; using Xunit; @@ -158,6 +159,126 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test Assert.Empty(bindingContext.ModelState); } + [Fact] + public async Task KeyValuePairModelBinder_DoesNotCreateCollection_ForTopLevelModel_OnFirstPass() + { + // Arrange + var binder = new KeyValuePairModelBinder(); + + var context = CreateContext(); + context.ModelName = "param"; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(KeyValuePair)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.Null(result); + } + + [Fact] + public async Task KeyValuePairModelBinder_CreatesEmptyCollection_ForTopLevelModel_OnFallback() + { + // Arrange + var binder = new KeyValuePairModelBinder(); + + var context = CreateContext(); + context.ModelName = string.Empty; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(KeyValuePair)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.NotNull(result); + + Assert.Equal(default(KeyValuePair), Assert.IsType>(result.Model)); + Assert.Equal(string.Empty, result.Key); + Assert.True(result.IsModelSet); + + Assert.Equal(result.ValidationNode.Model, result.Model); + Assert.Same(result.ValidationNode.Key, result.Key); + Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata); + } + + [Fact] + public async Task KeyValuePairModelBinder_CreatesEmptyCollection_ForTopLevelModel_WithExplicitPrefix() + { + // Arrange + var binder = new KeyValuePairModelBinder(); + + var context = CreateContext(); + context.ModelName = "prefix"; + context.BinderModelName = "prefix"; + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(KeyValuePair)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.NotNull(result); + + Assert.Equal(default(KeyValuePair), Assert.IsType>(result.Model)); + Assert.Equal("prefix", result.Key); + Assert.True(result.IsModelSet); + + Assert.Equal(result.ValidationNode.Model, result.Model); + Assert.Same(result.ValidationNode.Key, result.Key); + Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata); + } + + [Theory] + [InlineData("")] + [InlineData("param")] + public async Task KeyValuePairModelBinder_DoesNotCreateCollection_ForNonTopLevelModel(string prefix) + { + // Arrange + var binder = new KeyValuePairModelBinder(); + + var context = CreateContext(); + context.ModelName = ModelNames.CreatePropertyModelName(prefix, "KeyValuePairProperty"); + + var metadataProvider = context.OperationBindingContext.MetadataProvider; + context.ModelMetadata = metadataProvider.GetMetadataForProperty( + typeof(ModelWithKeyValuePairProperty), + nameof(ModelWithKeyValuePairProperty.KeyValuePairProperty)); + + context.ValueProvider = new TestValueProvider(new Dictionary()); + + // Act + var result = await binder.BindModelAsync(context); + + // Assert + Assert.Null(result); + } + + private static ModelBindingContext CreateContext() + { + var modelBindingContext = new ModelBindingContext() + { + OperationBindingContext = new OperationBindingContext() + { + HttpContext = new DefaultHttpContext(), + MetadataProvider = new TestModelMetadataProvider(), + ModelBinder = new TypeMatchModelBinder(), + } + }; + + return modelBindingContext; + } + private static ModelBindingContext GetBindingContext( IValueProvider valueProvider, IModelBinder innerBinder = null, @@ -210,6 +331,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test }); return mockStringBinder.Object; } + + private class ModelWithKeyValuePairProperty + { + public KeyValuePair KeyValuePairProperty { get; set; } + } } } #endif diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs index 94e59a69ff..0f810fffe0 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs @@ -138,7 +138,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal("11", entry.Value.RawValue); } - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task ArrayModelBinder_BindsArrayOfSimpleType_NoData() { // Arrange @@ -160,8 +160,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // This fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); Assert.Empty(Assert.IsType(modelBindingResult.Model)); Assert.Equal(0, modelState.Count); @@ -304,7 +304,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal("lang", entry.Value.RawValue); } - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task ArrayModelBinder_BindsArrayOfComplexType_NoData() { // Arrange @@ -326,8 +326,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // This fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); Assert.Empty(Assert.IsType(modelBindingResult.Model)); Assert.Equal(0, modelState.Count); diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/ByteArrayModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/ByteArrayModelBinderIntegrationTest.cs index 4203930dea..0087f5dbc4 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/ByteArrayModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/ByteArrayModelBinderIntegrationTest.cs @@ -76,7 +76,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); // Should be skipped. bug#2447 } - [Fact] + [Fact(Skip = "ByteArrayModelBinder should return a non-null result #2456")] public async Task BindParameter_NoData_DoesNotGetBound() { // Arrange @@ -102,11 +102,15 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests // Assert // ModelBindingResult - Assert.Null(modelBindingResult); + Assert.NotNull(modelBindingResult); // ModelState Assert.True(modelState.IsValid); Assert.Empty(modelState.Keys); + + Assert.Equal("CustomParameter", modelBindingResult.Key); + Assert.True(modelBindingResult.IsModelSet); + Assert.Equal(new byte[0], modelBindingResult.Model); } [Fact(Skip = "ModelState.Value not set due to #2445")] diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs index 30a25e1dd0..5eca2f79f7 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs @@ -145,7 +145,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal("11", entry.Value.RawValue); } - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task CollectionModelBinder_BindsListOfSimpleType_NoData() { // Arrange @@ -167,8 +167,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // This fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); Assert.Empty(Assert.IsType>(modelBindingResult.Model)); Assert.Equal(0, modelState.Count); @@ -311,7 +311,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal("11", entry.Value.RawValue); } - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task CollectionModelBinder_BindsListOfComplexType_NoData() { // Arrange @@ -333,8 +333,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // This fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); Assert.Empty(Assert.IsType>(modelBindingResult.Model)); Assert.Equal(0, modelState.Count); @@ -510,7 +510,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); } - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task CollectionModelBinder_BindsListOfComplexType_WithRequiredProperty_NoData() { // Arrange @@ -532,8 +532,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // This fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); Assert.Empty(Assert.IsType>(modelBindingResult.Model)); Assert.Equal(0, modelState.Count); diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs index 792e0b7157..7b72180e14 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs @@ -140,7 +140,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal("10", entry.Value.RawValue); } - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task DictionaryModelBinder_BindsDictionaryOfSimpleType_NoData() { // Arrange @@ -162,8 +162,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // This fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); Assert.Empty(Assert.IsType>(modelBindingResult.Model)); Assert.Equal(0, modelState.Count); @@ -309,7 +309,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal("10", entry.Value.RawValue); } - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task DictionaryModelBinder_BindsDictionaryOfComplexType_NoData() { // Arrange @@ -331,8 +331,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // This fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); Assert.Empty(Assert.IsType>(modelBindingResult.Model)); Assert.Equal(0, modelState.Count); diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs index 4a32af6ddc..b1a026d06e 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs @@ -99,7 +99,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests // // In this example we choose IFormCollection - because IFormCollection has a dedicated // model binder. - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task GenericModelBinder_BindsCollection_ElementTypeFromGreedyModelBinder_NoData() { // Arrange @@ -122,8 +122,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // This fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); var model = Assert.IsType>(modelBindingResult.Model); Assert.Empty(model); @@ -298,7 +298,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests // This is part of a random sampling of scenarios where a GenericModelBinder is used // recursively. - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task GenericModelBinder_BindsArrayOfDictionary_NoData() { // Arrange @@ -320,8 +320,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // This fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); var model = Assert.IsType[]>(modelBindingResult.Model); Assert.NotNull(model); @@ -424,7 +424,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests // This is part of a random sampling of scenarios where a GenericModelBinder is used // recursively. - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task GenericModelBinder_BindsCollectionOfKeyValuePair_NoData() { // Arrange @@ -446,8 +446,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // Fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); var model = Assert.IsType>>(modelBindingResult.Model); Assert.NotNull(model); @@ -559,7 +559,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests // This is part of a random sampling of scenarios where a GenericModelBinder is used // recursively. - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task GenericModelBinder_BindsDictionaryOfList_NoData() { // Arrange @@ -581,8 +581,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // Fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); var model = Assert.IsType>>(modelBindingResult.Model); Assert.NotNull(model); diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs index 230a1b34c8..58071797f7 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs @@ -139,7 +139,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal("10", entry.Value.RawValue); } - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task KeyValuePairModelBinder_BindsKeyValuePairOfSimpleType_NoData() { // Arrange @@ -161,10 +161,10 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // This fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); - Assert.Equal(new int[0], modelBindingResult.Model); + Assert.Equal(new KeyValuePair(), modelBindingResult.Model); Assert.Equal(0, modelState.Count); Assert.Equal(0, modelState.ErrorCount); @@ -306,7 +306,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal("10", entry.Value.RawValue); } - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task KeyValuePairModelBinder_BindsKeyValuePairOfComplexType_NoData() { // Arrange @@ -328,8 +328,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); // Assert - Assert.NotNull(modelBindingResult); // This fails due to #1579 - Assert.False(modelBindingResult.IsModelSet); + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); Assert.Equal(new KeyValuePair(), modelBindingResult.Model); diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/ValidationIntegrationTests.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/ValidationIntegrationTests.cs index 6b832ca281..dfccd8d93b 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/ValidationIntegrationTests.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/ValidationIntegrationTests.cs @@ -974,7 +974,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Null(error.Exception); } - [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + [Fact] public async Task Validation_StringLengthAttribute_OnProperyOfCollectionElement_NoData() { // Arrange @@ -997,14 +997,14 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests // Assert Assert.NotNull(modelBindingResult); - Assert.False(modelBindingResult.IsModelSet); + Assert.True(modelBindingResult.IsModelSet); var model = Assert.IsType>(modelBindingResult.Model); Assert.Empty(model); Assert.Equal(0, modelState.Count); Assert.Equal(0, modelState.ErrorCount); - Assert.False(modelState.IsValid); + Assert.True(modelState.IsValid); } private class Order11