From 502b832d178daa20832af82ac5c9b35ac45cf041 Mon Sep 17 00:00:00 2001 From: Harsh Gupta Date: Thu, 30 Apr 2015 12:06:31 -0700 Subject: [PATCH] =?UTF-8?q?Integration=20tests=20for=20Collection=20Model?= =?UTF-8?q?=20Binder=20->=20MutableObject(POCO)=20Model=20Binder=20?= =?UTF-8?q?=E2=80=93=20With-Data/Fallback-to-empty-prefix/Without-Data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ArrayModelBinderIntegrationTest.cs | 166 +++++++++++++++++ .../CollectionModelBinderIntegrationTest.cs | 166 +++++++++++++++++ .../DictionaryModelBinderIntegrationTest.cs | 170 ++++++++++++++++++ .../KeyValuePairModelBinderIntegrationTest.cs | 167 +++++++++++++++++ 4 files changed, 669 insertions(+) diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs index 3054e8732d..a9b30377d9 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs @@ -168,5 +168,171 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); } + + private class Person + { + public string Name { get; set; } + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task ArrayModelBinder_BindsArrayOfComplexType_WithPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Person[]) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?parameter[0].Name=bill¶meter[1].Name=lang"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType(modelBindingResult.Model); + Assert.Equal("bill", model[0].Name); + Assert.Equal("lang", model[1].Name); + + Assert.Equal(2, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0].Name").Value; + Assert.Equal("bill", entry.Value.AttemptedValue); + Assert.Equal("bill", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[1].Name").Value; + Assert.Equal("lang", entry.Value.AttemptedValue); + Assert.Equal("lang", entry.Value.RawValue); + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task ArrayModelBinder_BindsArrayOfComplexType_WithExplicitPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + BindingInfo = new BindingInfo() + { + BinderModelName = "prefix", + }, + ParameterType = typeof(Person[]) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?prefix[0].Name=bill&prefix[1]=lang"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType(modelBindingResult.Model); + Assert.Equal("bill", model[0].Name); + Assert.Equal("lang", model[1].Name); + + Assert.Equal(2, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0].Name").Value; + Assert.Equal("bill", entry.Value.AttemptedValue); + Assert.Equal("bill", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[1].Name").Value; + Assert.Equal("lang", entry.Value.AttemptedValue); + Assert.Equal("lang", entry.Value.RawValue); + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task ArrayModelBinder_BindsArrayOfComplexType_EmptyPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Person[]) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?[0].Name=bill&[1].Name=lang"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType(modelBindingResult.Model); + Assert.Equal("bill", model[0].Name); + Assert.Equal("lang", model[1].Name); + + Assert.Equal(2, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, kvp => kvp.Key == "[0].Name").Value; + Assert.Equal("bill", entry.Value.AttemptedValue); + Assert.Equal("bill", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "[1].Name").Value; + Assert.Equal("lang", entry.Value.AttemptedValue); + Assert.Equal("lang", entry.Value.RawValue); + } + + [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + public async Task ArrayModelBinder_BindsArrayOfComplexType_NoData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Person[]) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); // This fails due to #1579 + Assert.False(modelBindingResult.IsModelSet); + Assert.Empty(Assert.IsType(modelBindingResult.Model)); + + Assert.Equal(0, modelState.Count); + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs index 55b0b1db79..174f632c9b 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs @@ -172,5 +172,171 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); } + + private class Person + { + public int Id { get; set; } + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task CollectionModelBinder_BindsListOfComplexType_WithPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(List) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?parameter[0].Id=10¶meter[1].Id=11"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType>(modelBindingResult.Model); + Assert.Equal(10, model[0].Id); + Assert.Equal(11, model[1].Id); + + Assert.Equal(2, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0].Id").Value; + Assert.Equal("10", entry.Value.AttemptedValue); + Assert.Equal("10", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[1].Id").Value; + Assert.Equal("11", entry.Value.AttemptedValue); + Assert.Equal("11", entry.Value.RawValue); + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task CollectionModelBinder_BindsListOfComplexType_WithExplicitPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + BindingInfo = new BindingInfo() + { + BinderModelName = "prefix", + }, + ParameterType = typeof(List) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?prefix[0].Id=10&prefix[1].Id=11"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType>(modelBindingResult.Model); + Assert.Equal(10, model[0].Id); + Assert.Equal(11, model[1].Id); + + Assert.Equal(2, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0].Id").Value; + Assert.Equal("10", entry.Value.AttemptedValue); + Assert.Equal("10", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[1].Id").Value; + Assert.Equal("11", entry.Value.AttemptedValue); + Assert.Equal("11", entry.Value.RawValue); + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task CollectionModelBinder_BindsCollectionOfComplexType_EmptyPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(ICollection) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?[0].Id=10&[1].Id=11"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType>(modelBindingResult.Model); + Assert.Equal(10, model[0].Id); + Assert.Equal(11, model[1].Id); + + Assert.Equal(2, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, kvp => kvp.Key == "[0].Id").Value; + Assert.Equal("10", entry.Value.AttemptedValue); + Assert.Equal("10", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "[1].Id").Value; + Assert.Equal("11", entry.Value.AttemptedValue); + Assert.Equal("11", entry.Value.RawValue); + } + + [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + public async Task CollectionModelBinder_BindsListOfComplexType_NoData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(List) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); // This fails due to #1579 + Assert.False(modelBindingResult.IsModelSet); + Assert.Empty(Assert.IsType>(modelBindingResult.Model)); + + Assert.Equal(0, modelState.Count); + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs index 058f3669e0..63bc04b49c 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs @@ -2,6 +2,7 @@ // 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 System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Mvc.ModelBinding; @@ -169,5 +170,174 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); } + + private class Person + { + public int Id { get; set; } + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task DictionaryModelBinder_BindsDictionaryOfComplexType_WithPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Dictionary) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?parameter[0].Key=key0¶meter[0].Value.Id=10"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType>(modelBindingResult.Model); + Assert.Equal(1, model.Count); + Assert.Equal("key0", model.Keys.First()); + Assert.Equal(model.Values, model.Values); + + Assert.Equal(2, modelState.Count); // Fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0].Key").Value; + Assert.Equal("key0", entry.Value.AttemptedValue); + Assert.Equal("key0", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0].Value.Id").Value; + Assert.Equal("10", entry.Value.AttemptedValue); + Assert.Equal("10", entry.Value.RawValue); + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task DictionaryModelBinder_BindsDictionaryOfComplexType_WithExplicitPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + BindingInfo = new BindingInfo() + { + BinderModelName = "prefix", + }, + ParameterType = typeof(Dictionary) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?prefix[0].Key=key0&prefix[0].Value.Id=10"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType>(modelBindingResult.Model); + Assert.Equal(1, model.Count); + Assert.Equal("key0", model.Keys.First()); + Assert.Equal(model.Values, model.Values); + + Assert.Equal(2, modelState.Count); // Fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0].Key").Value; + Assert.Equal("key0", entry.Value.AttemptedValue); + Assert.Equal("key0", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0].Value.Id").Value; + Assert.Equal("10", entry.Value.AttemptedValue); + Assert.Equal("10", entry.Value.RawValue); + } + + [Fact(Skip = "Extra ModelState key because of #2446, IsValue == false because of #2470")] + public async Task DictionaryModelBinder_BindsDictionaryOfComplexType_EmptyPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Dictionary) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?[0].Key=key0&[0].Value.Id=10"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType>(modelBindingResult.Model); + Assert.Equal(1, model.Count); + Assert.Equal("key0", model.Keys.First()); + Assert.Equal(model.Values, model.Values); + + Assert.Equal(2, modelState.Count); // Fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); // Fails due to #2470 + + var entry = Assert.Single(modelState, kvp => kvp.Key == "[0].Key").Value; + Assert.Equal("key0", entry.Value.AttemptedValue); + Assert.Equal("key0", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "[0].Value.Id").Value; + Assert.Equal("10", entry.Value.AttemptedValue); + Assert.Equal("10", entry.Value.RawValue); + } + + [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + public async Task DictionaryModelBinder_BindsDictionaryOfComplexType_NoData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Dictionary) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); // This fails due to #1579 + Assert.False(modelBindingResult.IsModelSet); + Assert.Empty(Assert.IsType>(modelBindingResult.Model)); + + Assert.Equal(0, modelState.Count); + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs index c07bad66f6..7374e4097d 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs @@ -170,5 +170,172 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); } + + private class Person + { + public int Id { get; set; } + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task KeyValuePairModelBinder_BindsKeyValuePairOfComplexType_WithPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(KeyValuePair) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?parameter.Key=key0¶meter.Value.Id=10"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType>(modelBindingResult.Model); + Assert.Equal("key0", model.Key); + Assert.Equal(10, model.Value.Id); + + Assert.Equal(2, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Key").Value; + Assert.Equal("key0", entry.Value.AttemptedValue); + Assert.Equal("key0", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Value.Id").Value; + Assert.Equal("10", entry.Value.AttemptedValue); + Assert.Same(model.Value, entry.Value.RawValue); + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task KeyValuePairModelBinder_BindsKeyValuePairOfComplexType_WithExplicitPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + BindingInfo = new BindingInfo() + { + BinderModelName = "prefix", + }, + ParameterType = typeof(KeyValuePair) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?prefix.Key=key0&prefix.Value.Id=10"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType>(modelBindingResult.Model); + Assert.Equal("key0", model.Key); + Assert.Equal(10, model.Value.Id); + + Assert.Equal(2, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix.Key").Value; + Assert.Equal("key0", entry.Value.AttemptedValue); + Assert.Equal("key0", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "prefix.Value.Id").Value; + Assert.Equal("10", entry.Value.AttemptedValue); + Assert.Equal("10", entry.Value.RawValue); + } + + [Fact(Skip = "Extra ModelState key because of #2446")] + public async Task KeyValuePairModelBinder_BindsKeyValuePairOfComplexType_EmptyPrefix_Success() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(KeyValuePair) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?Key=key0&Value.Id=10"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + var model = Assert.IsType>(modelBindingResult.Model); + Assert.Equal("key0", model.Key); + Assert.Equal(10, model.Value.Id); + + Assert.Equal(2, modelState.Count); // This fails due to #2446 + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + + var entry = Assert.Single(modelState, kvp => kvp.Key == "Key").Value; + Assert.Equal("key0", entry.Value.AttemptedValue); + Assert.Equal("key0", entry.Value.RawValue); + + entry = Assert.Single(modelState, kvp => kvp.Key == "Value.Id").Value; + Assert.Equal("10", entry.Value.AttemptedValue); + Assert.Equal("10", entry.Value.RawValue); + } + + [Fact(Skip = "Empty collection should be created by the collection model binder #1579")] + public async Task KeyValuePairModelBinder_BindsKeyValuePairOfComplexType_NoData() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(KeyValuePair) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + Assert.NotNull(modelBindingResult); // This fails due to #1579 + Assert.False(modelBindingResult.IsModelSet); + + Assert.Equal(new KeyValuePair(), modelBindingResult.Model); + + Assert.Equal(0, modelState.Count); + Assert.Equal(0, modelState.ErrorCount); + Assert.True(modelState.IsValid); + } } } \ No newline at end of file