// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Collections; using Microsoft.AspNet.Mvc.ModelBinding; using Xunit; namespace Microsoft.AspNet.Mvc.IntegrationTests { // Integration tests targeting the behavior of the MutableObjectModelBinder and related classes // with other model binders. public class MutableObjectModelBinderIntegrationTest { private const string AddressBodyContent = "{ \"street\" : \"" + AddressStreetContent + "\" }"; private const string AddressStreetContent = "1 Microsoft Way"; private static readonly byte[] ByteArrayContent = Encoding.BigEndianUnicode.GetBytes("abcd"); private static readonly string ByteArrayEncoded = Convert.ToBase64String(ByteArrayContent); private class Order1 { public int ProductId { get; set; } public Person1 Customer { get; set; } } private class Person1 { public string Name { get; set; } [FromBody] public Address1 Address { get; set; } } private class Address1 { public string Street { get; set; } } [Fact(Skip = "Extra entries in model state #2446, ModelState.Value not set due to #2445.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_WithPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order1) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Customer.Name=bill"); SetJsonBodyContent(request, AddressBodyContent); }); 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.NotNull(model.Customer); Assert.Equal("bill", model.Customer.Name); Assert.NotNull(model.Customer.Address); Assert.Equal(AddressStreetContent, model.Customer.Address.Street); Assert.Equal(2, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); // These fail due to #2445 entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Address").Value; Assert.Null(entry.Value.AttemptedValue); // ModelState entries for body don't include original text. Assert.Same(model.Customer.Address, entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446, ModelState.Value not set due to #2445.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_WithEmptyPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order1) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?Customer.Name=bill"); SetJsonBodyContent(request, AddressBodyContent); }); 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.NotNull(model.Customer); Assert.Equal("bill", model.Customer.Name); Assert.NotNull(model.Customer.Address); Assert.Equal(AddressStreetContent, model.Customer.Address.Street); Assert.Equal(2, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Customer.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); // These fail due to #2445 entry = Assert.Single(modelState, e => e.Key == "Customer.Address").Value; Assert.Null(entry.Value.AttemptedValue); // ModelState entries for body don't include original text. Assert.Same(model.Customer.Address, entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_WithPrefix_NoBodyData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order1) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Customer.Name=bill"); }); 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.NotNull(model.Customer); Assert.Equal("bill", model.Customer.Name); Assert.Null(model.Customer.Address); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there is // body data in the request, it won't be used. [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_WithPrefix_PartialData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order1) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.ProductId=10"); SetJsonBodyContent(request, AddressBodyContent); }); 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.Null(model.Customer); Assert.Equal(10, model.ProductId); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.ProductId").Value; Assert.Equal("10", entry.Value.AttemptedValue); Assert.Equal("10", entry.Value.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there is // body data in the request, it won't be used. [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_WithPrefix_NoData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order1) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?"); SetJsonBodyContent(request, AddressBodyContent); }); 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.Null(model.Customer); Assert.Equal(0, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); } private class Order2 { public int ProductId { get; set; } public Person2 Customer { get; set; } } private class Person2 { public string Name { get; set; } [FromServices] public IScopedInstance BindingContext { get; set; } } [Fact(Skip = "Extra entries in model state #2446, #2646.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithServicesModelBinder_WithPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order2) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Customer.Name=bill"); }); 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.NotNull(model.Customer); Assert.Equal("bill", model.Customer.Name); Assert.NotNull(model.Customer.BindingContext); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446, #2646.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithServicesModelBinder_WithEmptyPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order2) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?Customer.Name=bill"); }); 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.NotNull(model.Customer); Assert.Equal("bill", model.Customer.Name); Assert.NotNull(model.Customer.BindingContext); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Customer.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there is // a [FromServices], it won't be used. [Fact(Skip = "Extra entries in model state #2446, #2646.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithServicesModelBinder_WithPrefix_PartialData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order2) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.ProductId=10"); SetJsonBodyContent(request, AddressBodyContent); }); 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.Null(model.Customer); Assert.Equal(10, model.ProductId); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.ProductId").Value; Assert.Equal("10", entry.Value.AttemptedValue); Assert.Equal("10", entry.Value.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there is // a [FromServices], it won't be used. [Fact(Skip = "Extra entries in model state #2446, #2646.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithServicesModelBinder_WithPrefix_NoData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order2) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?"); SetJsonBodyContent(request, AddressBodyContent); }); 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.Null(model.Customer); Assert.Equal(0, modelState.Count); // This fails due to #2446, #2464 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); } private class Order3 { public int ProductId { get; set; } public Person3 Customer { get; set; } } private class Person3 { public string Name { get; set; } public byte[] Token { get; set; } } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithByteArrayModelBinder_WithPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order3) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Customer.Name=bill¶meter.Customer.Token=" + ByteArrayEncoded); }); 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.NotNull(model.Customer); Assert.Equal("bill", model.Customer.Name); Assert.Equal(ByteArrayContent, model.Customer.Token); Assert.Equal(2, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); // These fail due to #2445 entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Token").Value; Assert.Equal(ByteArrayEncoded, entry.Value.AttemptedValue); Assert.Equal(ByteArrayEncoded, entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithByteArrayModelBinder_WithEmptyPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order3) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?Customer.Name=bill&Customer.Token=" + ByteArrayEncoded); }); 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.NotNull(model.Customer); Assert.Equal("bill", model.Customer.Name); Assert.Equal(ByteArrayContent, model.Customer.Token); Assert.Equal(2, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Customer.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "Customer.Token").Value; Assert.Equal(ByteArrayEncoded, entry.Value.AttemptedValue); Assert.Equal(ByteArrayEncoded, entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithByteArrayModelBinder_WithPrefix_NoData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order1) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Customer.Name=bill"); }); 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.NotNull(model.Customer); Assert.Equal("bill", model.Customer.Name); Assert.Null(model.Customer.Address); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); } private class Order4 { public int ProductId { get; set; } public Person4 Customer { get; set; } } private class Person4 { public string Name { get; set; } public IEnumerable Documents { get; set; } } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBinder_WithPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order4) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Customer.Name=bill"); SetFormFileBodyContent(request, "Hello, World!", "parameter.Customer.Documents"); }); 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.NotNull(model.Customer); Assert.Equal("bill", model.Customer.Name); Assert.Single(model.Customer.Documents); Assert.Equal(2, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Documents").Value; Assert.Null(entry.Value.AttemptedValue); // FormFile entries for body don't include original text. Assert.Same(model.Customer.Documents, entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBinder_WithEmptyPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order4) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?Customer.Name=bill"); SetFormFileBodyContent(request, "Hello, World!", "Customer.Documents"); }); 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.NotNull(model.Customer); Assert.Equal("bill", model.Customer.Name); Assert.Single(model.Customer.Documents); Assert.Equal(2, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Customer.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "Customer.Documents").Value; Assert.Null(entry.Value.AttemptedValue); // FormFile entries for body don't include original text. Assert.Same(model.Customer.Documents, entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBinder_WithPrefix_NoBodyData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order4) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Customer.Name=bill"); // Deliberately leaving out any form data. }); 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.NotNull(model.Customer); Assert.Equal("bill", model.Customer.Name); Assert.Null(model.Customer.Documents); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there are // form files in the request, it won't be used. [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBinder_WithPrefix_PartialData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order4) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.ProductId=10"); SetFormFileBodyContent(request, "Hello, World!", "parameter.Customer.Documents"); }); 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.Null(model.Customer); Assert.Equal(10, model.ProductId); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.ProductId").Value; Assert.Equal("10", entry.Value.AttemptedValue); Assert.Equal("10", entry.Value.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there is // body data in the request, it won't be used. [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBinder_WithPrefix_NoData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order4) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?"); SetFormFileBodyContent(request, "Hello, World!", "parameter.Customer.Documents"); }); 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.Null(model.Customer); Assert.Equal(0, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); } private class Order5 { public string Name { get; set; } public int[] ProductIds { get; set; } } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsArrayProperty_WithPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order5) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Name=bill¶meter.ProductIds[0]=10¶meter.ProductIds[1]=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("bill", model.Name); Assert.Equal(new int[] { 10, 11 }, model.ProductIds); Assert.Equal(3, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductIds[0]").Value; Assert.Equal("10", entry.Value.AttemptedValue); Assert.Equal("10", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductIds[1]").Value; Assert.Equal("11", entry.Value.AttemptedValue); Assert.Equal("11", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsArrayProperty_EmptyPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order5) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?Name=bill&ProductIds[0]=10&ProductIds[1]=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("bill", model.Name); Assert.Equal(new int[] { 10, 11 }, model.ProductIds); Assert.Equal(3, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[0]").Value; Assert.Equal("10", entry.Value.AttemptedValue); Assert.Equal("10", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[1]").Value; Assert.Equal("11", entry.Value.AttemptedValue); Assert.Equal("11", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsArrayProperty_NoCollectionData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order5) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Name=bill"); }); 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.Name); Assert.Null(model.ProductIds); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsArrayProperty_NoData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order5) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. 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); Assert.True(modelBindingResult.IsModelSet); var model = Assert.IsType(modelBindingResult.Model); Assert.Null(model.Name); Assert.Null(model.ProductIds); Assert.Equal(0, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); } private class Order6 { public string Name { get; set; } public List ProductIds { get; set; } } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsListProperty_WithPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order6) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Name=bill¶meter.ProductIds[0]=10¶meter.ProductIds[1]=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("bill", model.Name); Assert.Equal(new List() { 10, 11 }, model.ProductIds); Assert.Equal(3, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductIds[0]").Value; Assert.Equal("10", entry.Value.AttemptedValue); Assert.Equal("10", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductIds[1]").Value; Assert.Equal("11", entry.Value.AttemptedValue); Assert.Equal("11", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsListProperty_EmptyPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order6) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?Name=bill&ProductIds[0]=10&ProductIds[1]=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("bill", model.Name); Assert.Equal(new List() { 10, 11 }, model.ProductIds); Assert.Equal(3, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[0]").Value; Assert.Equal("10", entry.Value.AttemptedValue); Assert.Equal("10", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[1]").Value; Assert.Equal("11", entry.Value.AttemptedValue); Assert.Equal("11", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsListProperty_NoCollectionData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order6) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Name=bill"); }); 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.Name); Assert.Null(model.ProductIds); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsListProperty_NoData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order6) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. 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); Assert.True(modelBindingResult.IsModelSet); var model = Assert.IsType(modelBindingResult.Model); Assert.Null(model.Name); Assert.Null(model.ProductIds); Assert.Equal(0, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); } private class Order7 { public string Name { get; set; } public Dictionary ProductIds { get; set; } } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsDictionaryProperty_WithPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order7) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Name=bill¶meter.ProductIds[0].Key=key0¶meter.ProductIds[0].Value=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("bill", model.Name); Assert.Equal(new Dictionary() { { "key0", 10 } }, model.ProductIds); Assert.Equal(3, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductIds[0].Key").Value; Assert.Equal("key0", entry.Value.AttemptedValue); Assert.Equal("key0", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductIds[0].Value").Value; Assert.Equal("10", entry.Value.AttemptedValue); Assert.Equal("10", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsDictionaryProperty_EmptyPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order7) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?Name=bill&ProductIds[0].Key=key0&ProductIds[0].Value=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("bill", model.Name); Assert.Equal(new Dictionary() { { "key0", 10 } }, model.ProductIds); Assert.Equal(3, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[0].Key").Value; Assert.Equal("key0", entry.Value.AttemptedValue); Assert.Equal("key0", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[0].Value").Value; Assert.Equal("10", entry.Value.AttemptedValue); Assert.Equal("10", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsDictionaryProperty_NoCollectionData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order7) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Name=bill"); }); 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.Name); Assert.Null(model.ProductIds); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsDictionaryProperty_NoData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order7) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. 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); Assert.True(modelBindingResult.IsModelSet); var model = Assert.IsType(modelBindingResult.Model); Assert.Null(model.Name); Assert.Null(model.ProductIds); Assert.Equal(0, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); } private class Order8 { public string Name { get; set; } public KeyValuePair ProductId { get; set; } } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsKeyValuePairProperty_WithPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order8) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Name=bill¶meter.ProductId.Key=key0¶meter.ProductId.Value=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("bill", model.Name); Assert.Equal(new KeyValuePair("key0", 10), model.ProductId); Assert.Equal(3, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductId.Key").Value; Assert.Equal("key0", entry.Value.AttemptedValue); Assert.Equal("key0", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductId.Value").Value; Assert.Equal("10", entry.Value.AttemptedValue); Assert.Equal("10", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsKeyValuePairProperty_EmptyPrefix_Success() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order8) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?Name=bill&ProductId.Key=key0&ProductId.Value=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("bill", model.Name); Assert.Equal(new KeyValuePair("key0", 10), model.ProductId); Assert.Equal(3, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductId.Key").Value; Assert.Equal("key0", entry.Value.AttemptedValue); Assert.Equal("key0", entry.Value.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductId.Value").Value; Assert.Equal("10", entry.Value.AttemptedValue); Assert.Equal("10", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsKeyValuePairProperty_NoCollectionData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order8) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?parameter.Name=bill"); }); 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.Name); Assert.Equal(default(KeyValuePair), model.ProductId); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Name").Value; Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.RawValue); } [Fact(Skip = "Extra entries in model state #2446.")] public async Task MutableObjectModelBinder_BindsKeyValuePairProperty_NoData() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order8) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. 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); Assert.True(modelBindingResult.IsModelSet); var model = Assert.IsType(modelBindingResult.Model); Assert.Null(model.Name); Assert.Equal(default(KeyValuePair), model.ProductId); Assert.Equal(0, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); } private class Order9 { public Person9 Customer { get; set; } } private class Person9 { [FromBody] public Address1 Address { get; set; } [FromServices] public IScopedInstance BindingContext { get; set; } } // If a nested POCO object has all properties bound from a greedy source, then it should be populated // if the top-level object is created. [Fact(Skip = "Extra entries in model state #2446, ModelState.Value not set due to #2445.")] public async Task MutableObjectModelBinder_BindsNestedPOCO_WithAllGreedyBoundProperties() { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor() { Name = "parameter", ParameterType = typeof(Order9) }; // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request.QueryString = new QueryString("?"); SetJsonBodyContent(request, AddressBodyContent); }); 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.NotNull(model.Customer); Assert.NotNull(model.Customer.BindingContext); Assert.NotNull(model.Customer.Address); Assert.Equal(AddressStreetContent, model.Customer.Address.Street); Assert.Equal(1, modelState.Count); // This fails due to #2446 Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Customer.Address").Value; Assert.Null(entry.Value.AttemptedValue); // This fails due to #2445 Assert.Same(model.Customer.Address, entry.Value.RawValue); } private static void SetJsonBodyContent(HttpRequest request, string content) { var stream = new MemoryStream(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false).GetBytes(content)); request.Body = stream; request.ContentType = "application/json"; } private static void SetFormFileBodyContent(HttpRequest request, string content, string name) { var fileCollection = new FormFileCollection(); var formCollection = new FormCollection(new Dictionary(), fileCollection); var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(content)); request.Form = formCollection; request.ContentType = "multipart/form-data"; request.Headers["Content-Disposition"] = "form-data; name=" + name + "; filename=text.txt"; fileCollection.Add(new FormFile(memoryStream, 0, memoryStream.Length) { Headers = request.Headers }); } } }