Fix #1579 - Bind top-level collections as an empty collection
This change treats 'top-level' collection-type models similarly to top-level POCO model - namely that they will always be instantiated even if there's no data to put inside.
This commit is contained in:
parent
b64fd7ae39
commit
8f38650d1f
|
|
@ -25,6 +25,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return base.BindModelAsync(bindingContext);
|
||||
}
|
||||
|
||||
protected override object CreateEmptyCollection()
|
||||
{
|
||||
return new TElement[0];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override object GetModel(IEnumerable<TElement> newCollection)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<TElement>();
|
||||
}
|
||||
|
||||
// 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<CollectionResult> 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)
|
||||
|
|
|
|||
|
|
@ -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<TKey, TValue>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<TKey, TValue>();
|
||||
|
||||
var validationNode = new ModelValidationNode(
|
||||
bindingContext.ModelName,
|
||||
bindingContext.ModelMetadata,
|
||||
model);
|
||||
|
||||
return new ModelBindingResult(
|
||||
model,
|
||||
bindingContext.ModelName,
|
||||
isModelSet: true,
|
||||
validationNode: validationNode);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<int>();
|
||||
var binder = new ArrayModelBinder<string>();
|
||||
|
||||
var context = CreateContext();
|
||||
context.ModelName = "param";
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(string[]));
|
||||
|
||||
context.ValueProvider = new TestValueProvider(new Dictionary<string, object>());
|
||||
|
||||
// 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<string>();
|
||||
|
||||
var context = CreateContext();
|
||||
context.ModelName = string.Empty;
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(string[]));
|
||||
|
||||
context.ValueProvider = new TestValueProvider(new Dictionary<string, object>());
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Empty(Assert.IsType<string[]>(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<string>();
|
||||
|
||||
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<string, object>());
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Empty(Assert.IsType<string[]>(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<string>();
|
||||
|
||||
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<string, object>());
|
||||
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -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<string>();
|
||||
|
||||
var context = CreateContext();
|
||||
context.ModelName = "param";
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(List<string>));
|
||||
|
||||
context.ValueProvider = new TestValueProvider(new Dictionary<string, object>());
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CollectionModelBinder_CreatesEmptyCollection_ForTopLevelModel_OnFallback()
|
||||
{
|
||||
// Arrange
|
||||
var binder = new CollectionModelBinder<string>();
|
||||
|
||||
var context = CreateContext();
|
||||
context.ModelName = string.Empty;
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(List<string>));
|
||||
|
||||
context.ValueProvider = new TestValueProvider(new Dictionary<string, object>());
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Empty(Assert.IsType<List<string>>(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<string>();
|
||||
|
||||
var context = CreateContext();
|
||||
context.ModelName = "prefix";
|
||||
context.BinderModelName = "prefix";
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(List<string>));
|
||||
|
||||
context.ValueProvider = new TestValueProvider(new Dictionary<string, object>());
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Empty(Assert.IsType<List<string>>(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<string>();
|
||||
|
||||
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<string, object>());
|
||||
|
||||
// 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<string> ListProperty { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<string, string>();
|
||||
|
||||
var context = CreateContext();
|
||||
context.ModelName = "param";
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(Dictionary<string, string>));
|
||||
|
||||
context.ValueProvider = new TestValueProvider(new Dictionary<string, object>());
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DictionaryModelBinder_CreatesEmptyCollection_ForTopLevelModel_OnFallback()
|
||||
{
|
||||
// Arrange
|
||||
var binder = new DictionaryModelBinder<string, string>();
|
||||
|
||||
var context = CreateContext();
|
||||
context.ModelName = string.Empty;
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(Dictionary<string, string>));
|
||||
|
||||
context.ValueProvider = new TestValueProvider(new Dictionary<string, object>());
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Empty(Assert.IsType<Dictionary<string, string>>(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<string, string>();
|
||||
|
||||
var context = CreateContext();
|
||||
context.ModelName = "prefix";
|
||||
context.BinderModelName = "prefix";
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(Dictionary<string, string>));
|
||||
|
||||
context.ValueProvider = new TestValueProvider(new Dictionary<string, object>());
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Empty(Assert.IsType<Dictionary<string, string>>(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<string, string>();
|
||||
|
||||
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<string, object>());
|
||||
|
||||
// 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<string, string> DictionaryProperty { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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<string, string>();
|
||||
|
||||
var context = CreateContext();
|
||||
context.ModelName = "param";
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(KeyValuePair<string, string>));
|
||||
|
||||
context.ValueProvider = new TestValueProvider(new Dictionary<string, object>());
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task KeyValuePairModelBinder_CreatesEmptyCollection_ForTopLevelModel_OnFallback()
|
||||
{
|
||||
// Arrange
|
||||
var binder = new KeyValuePairModelBinder<string, string>();
|
||||
|
||||
var context = CreateContext();
|
||||
context.ModelName = string.Empty;
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(KeyValuePair<string, string>));
|
||||
|
||||
context.ValueProvider = new TestValueProvider(new Dictionary<string, object>());
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Equal(default(KeyValuePair<string, string>), Assert.IsType<KeyValuePair<string, string>>(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<string, string>();
|
||||
|
||||
var context = CreateContext();
|
||||
context.ModelName = "prefix";
|
||||
context.BinderModelName = "prefix";
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(KeyValuePair<string, string>));
|
||||
|
||||
context.ValueProvider = new TestValueProvider(new Dictionary<string, object>());
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
|
||||
Assert.Equal(default(KeyValuePair<string, string>), Assert.IsType<KeyValuePair<string, string>>(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<string, string>();
|
||||
|
||||
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<string, object>());
|
||||
|
||||
// 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<string, string> KeyValuePairProperty { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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<int[]>(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<Person[]>(modelBindingResult.Model));
|
||||
|
||||
Assert.Equal(0, modelState.Count);
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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<List<int>>(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<List<Person>>(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<List<Person2>>(modelBindingResult.Model));
|
||||
|
||||
Assert.Equal(0, modelState.Count);
|
||||
|
|
|
|||
|
|
@ -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<Dictionary<string, int>>(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<Dictionary<string, Person>>(modelBindingResult.Model));
|
||||
|
||||
Assert.Equal(0, modelState.Count);
|
||||
|
|
|
|||
|
|
@ -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<List<IFormCollection>>(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<Dictionary<string, int>[]>(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<Collection<KeyValuePair<string, int>>>(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<Dictionary<string, List<int>>>(modelBindingResult.Model);
|
||||
Assert.NotNull(model);
|
||||
|
|
|
|||
|
|
@ -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<string, int>(), 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<string, Person>(), modelBindingResult.Model);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<List<Order10>>(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
|
||||
|
|
|
|||
Loading…
Reference in New Issue