Add support for model binding dictionaries from `prefix[name]=value` entries
- #1418 - add new fallback binding in `DictionaryModelBinder` - similar to MVC 5 approach but more explicit and with better key conversion support - fix bugs in `PrefixContainer` encountered while adding new tests of #1418 scenarios - did not handle entries like "[key]" or "prefix.key[index]" correctly - refactor part of `GetKeyFromEmptyPrefix()` into `IndexOfDelimiter()`; share with `GetKeyFromNonEmptyPrefix()` - extend `ReadableStringCollectionValueProviderTest` to cover bracketed key segments nits: - remove use of "foo", "bar", and "baz" in affected test classes - `""` -> `string.Empty` - `vpResult` -> `result`
This commit is contained in:
parent
6033679193
commit
79a2982441
|
|
@ -2,7 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
|
|
@ -13,15 +16,88 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// <typeparam name="TValue">Type of values in the dictionary.</typeparam>
|
||||
public class DictionaryModelBinder<TKey, TValue> : CollectionModelBinder<KeyValuePair<TKey, TValue>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext)
|
||||
{
|
||||
var result = await base.BindModelAsync(bindingContext);
|
||||
if (result == null || !result.IsModelSet)
|
||||
{
|
||||
// No match for the prefix at all.
|
||||
return result;
|
||||
}
|
||||
|
||||
Debug.Assert(result.Model != null);
|
||||
var model = (Dictionary<TKey, TValue>)result.Model;
|
||||
if (model.Count != 0)
|
||||
{
|
||||
// ICollection<KeyValuePair<TKey, TValue>> approach was successful.
|
||||
return result;
|
||||
}
|
||||
|
||||
var enumerableValueProvider = bindingContext.ValueProvider as IEnumerableValueProvider;
|
||||
if (enumerableValueProvider == null)
|
||||
{
|
||||
// No IEnumerableValueProvider available for the fallback approach. For example the user may have
|
||||
// replaced the ValueProvider with something other than a CompositeValueProvider.
|
||||
return result;
|
||||
}
|
||||
|
||||
// Attempt to bind dictionary from a set of prefix[key]=value entries. Get the short and long keys first.
|
||||
var keys = await enumerableValueProvider.GetKeysFromPrefixAsync(bindingContext.ModelName);
|
||||
if (!keys.Any())
|
||||
{
|
||||
// No entries with the expected keys.
|
||||
return result;
|
||||
}
|
||||
|
||||
// Update the existing successful but empty ModelBindingResult.
|
||||
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
|
||||
var valueMetadata = metadataProvider.GetMetadataForType(typeof(TValue));
|
||||
var valueBindingContext = ModelBindingContext.GetChildModelBindingContext(
|
||||
bindingContext,
|
||||
bindingContext.ModelName,
|
||||
valueMetadata);
|
||||
|
||||
var modelBinder = bindingContext.OperationBindingContext.ModelBinder;
|
||||
var validationNode = result.ValidationNode;
|
||||
|
||||
foreach (var key in keys)
|
||||
{
|
||||
var dictionaryKey = ConvertFromString(key.Key);
|
||||
valueBindingContext.ModelName = key.Value;
|
||||
|
||||
var valueResult = await modelBinder.BindModelAsync(valueBindingContext);
|
||||
|
||||
// Always add an entry to the dictionary but validate only if binding was successful.
|
||||
model[dictionaryKey] = ModelBindingHelper.CastOrDefault<TValue>(valueResult?.Model);
|
||||
if (valueResult != null && valueResult.IsModelSet)
|
||||
{
|
||||
validationNode.ChildNodes.Add(valueResult.ValidationNode);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override object GetModel(IEnumerable<KeyValuePair<TKey, TValue>> newCollection)
|
||||
{
|
||||
return newCollection?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override object CreateEmptyCollection()
|
||||
{
|
||||
return new Dictionary<TKey, TValue>();
|
||||
}
|
||||
|
||||
private static TKey ConvertFromString(string keyString)
|
||||
{
|
||||
// Use InvariantCulture to convert string since ExpressionHelper.GetExpressionText() used that culture.
|
||||
var keyResult = new ValueProviderResult(keyString);
|
||||
var keyObject = keyResult.ConvertTo(typeof(TKey));
|
||||
|
||||
return ModelBindingHelper.CastOrDefault<TKey>(keyObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,31 +81,34 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
private static void GetKeyFromEmptyPrefix(string entry, IDictionary<string, string> results)
|
||||
{
|
||||
var dotPosition = entry.IndexOf('.');
|
||||
var bracketPosition = entry.IndexOf('[');
|
||||
var delimiterPosition = -1;
|
||||
string key;
|
||||
string fullName;
|
||||
var delimiterPosition = IndexOfDelimiter(entry, 0);
|
||||
|
||||
if (dotPosition == -1)
|
||||
if (delimiterPosition == 0 && entry[0] == '[')
|
||||
{
|
||||
if (bracketPosition != -1)
|
||||
// Handle an entry such as "[key]".
|
||||
var bracketPosition = entry.IndexOf(']', 1);
|
||||
if (bracketPosition == -1)
|
||||
{
|
||||
delimiterPosition = bracketPosition;
|
||||
// Malformed for dictionary.
|
||||
return;
|
||||
}
|
||||
|
||||
key = entry.Substring(1, bracketPosition - 1);
|
||||
fullName = entry.Substring(0, bracketPosition + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bracketPosition == -1)
|
||||
{
|
||||
delimiterPosition = dotPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
delimiterPosition = Math.Min(dotPosition, bracketPosition);
|
||||
}
|
||||
// Handle an entry such as "key", "key.property" and "key[index]".
|
||||
key = delimiterPosition == -1 ? entry : entry.Substring(0, delimiterPosition);
|
||||
fullName = key;
|
||||
}
|
||||
|
||||
var key = delimiterPosition == -1 ? entry : entry.Substring(0, delimiterPosition);
|
||||
results[key] = key;
|
||||
if (!results.ContainsKey(key))
|
||||
{
|
||||
results.Add(key, fullName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetKeyFromNonEmptyPrefix(string prefix, string entry, IDictionary<string, string> results)
|
||||
|
|
@ -117,17 +120,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
switch (entry[prefix.Length])
|
||||
{
|
||||
case '.':
|
||||
var dotPosition = entry.IndexOf('.', keyPosition);
|
||||
if (dotPosition == -1)
|
||||
// Handle an entry such as "prefix.key", "prefix.key.property" and "prefix.key[index]".
|
||||
var delimiterPosition = IndexOfDelimiter(entry, keyPosition);
|
||||
if (delimiterPosition == -1)
|
||||
{
|
||||
dotPosition = entry.Length;
|
||||
// Neither '.' nor '[' found later in the name. Use rest of the string.
|
||||
key = entry.Substring(keyPosition);
|
||||
fullName = entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
key = entry.Substring(keyPosition, delimiterPosition - keyPosition);
|
||||
fullName = entry.Substring(0, delimiterPosition);
|
||||
}
|
||||
|
||||
key = entry.Substring(keyPosition, dotPosition - keyPosition);
|
||||
fullName = entry.Substring(0, dotPosition);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
// Handle an entry such as "prefix[key]".
|
||||
var bracketPosition = entry.IndexOf(']', keyPosition);
|
||||
if (bracketPosition == -1)
|
||||
{
|
||||
|
|
@ -140,6 +149,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
break;
|
||||
|
||||
default:
|
||||
// Ignore an entry such as "prefixA".
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -188,6 +198,28 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
private static int IndexOfDelimiter(string entry, int startIndex)
|
||||
{
|
||||
int delimiterPosition;
|
||||
var bracketPosition = entry.IndexOf('[', startIndex);
|
||||
var dotPosition = entry.IndexOf('.', startIndex);
|
||||
|
||||
if (dotPosition == -1)
|
||||
{
|
||||
delimiterPosition = bracketPosition;
|
||||
}
|
||||
else if (bracketPosition == -1)
|
||||
{
|
||||
delimiterPosition = dotPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
delimiterPosition = Math.Min(dotPosition, bracketPosition);
|
||||
}
|
||||
|
||||
return delimiterPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert an ICollection to an array, removing null values. Fast path for case where
|
||||
/// there are no null values.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if DNX451
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Moq;
|
||||
|
|
@ -64,6 +67,200 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Equal("eighty-four", dictionary[84]);
|
||||
}
|
||||
|
||||
// modelName, keyFormat, dictionary
|
||||
public static TheoryData<string, string, IDictionary<string, string>> StringToStringData
|
||||
{
|
||||
get
|
||||
{
|
||||
var dictionaryWithOne = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
{ "one", "one" },
|
||||
};
|
||||
var dictionaryWithThree = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
{ "one", "one" },
|
||||
{ "two", "two" },
|
||||
{ "three", "three" },
|
||||
};
|
||||
|
||||
return new TheoryData<string, string, IDictionary<string, string>>
|
||||
{
|
||||
{ string.Empty, "[{0}]", dictionaryWithOne },
|
||||
{ string.Empty, "[{0}]", dictionaryWithThree },
|
||||
{ "prefix", "prefix[{0}]", dictionaryWithOne },
|
||||
{ "prefix", "prefix[{0}]", dictionaryWithThree },
|
||||
{ "prefix.property", "prefix.property[{0}]", dictionaryWithOne },
|
||||
{ "prefix.property", "prefix.property[{0}]", dictionaryWithThree },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(StringToStringData))]
|
||||
public async Task BindModel_FallsBackToBindingValues(
|
||||
string modelName,
|
||||
string keyFormat,
|
||||
IDictionary<string, string> dictionary)
|
||||
{
|
||||
// Arrange
|
||||
var binder = new DictionaryModelBinder<string, string>();
|
||||
var context = CreateContext();
|
||||
context.ModelName = modelName;
|
||||
context.OperationBindingContext.ModelBinder = CreateCompositeBinder();
|
||||
context.OperationBindingContext.ValueProvider = CreateEnumerableValueProvider(keyFormat, dictionary);
|
||||
context.ValueProvider = context.OperationBindingContext.ValueProvider;
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ModelWithDictionaryProperty),
|
||||
nameof(ModelWithDictionaryProperty.DictionaryProperty));
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.False(result.IsFatalError);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Equal(modelName, result.Key);
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
|
||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<string, string>>(result.Model);
|
||||
Assert.Equal(dictionary, resultDictionary);
|
||||
}
|
||||
|
||||
// Similar to one BindModel_FallsBackToBindingValues case but without an IEnumerableValueProvider.
|
||||
[Fact]
|
||||
public async Task BindModel_DoesNotFallBack_WithoutEnumerableValueProvider()
|
||||
{
|
||||
// Arrange
|
||||
var dictionary = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
{ "one", "one" },
|
||||
{ "two", "two" },
|
||||
{ "three", "three" },
|
||||
};
|
||||
|
||||
var binder = new DictionaryModelBinder<string, string>();
|
||||
var context = CreateContext();
|
||||
context.ModelName = "prefix";
|
||||
context.OperationBindingContext.ModelBinder = CreateCompositeBinder();
|
||||
context.OperationBindingContext.ValueProvider = CreateTestValueProvider("prefix[{0}]", dictionary);
|
||||
context.ValueProvider = context.OperationBindingContext.ValueProvider;
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ModelWithDictionaryProperty),
|
||||
nameof(ModelWithDictionaryProperty.DictionaryProperty));
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.False(result.IsFatalError);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Equal("prefix", result.Key);
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
|
||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<string, string>>(result.Model);
|
||||
Assert.Empty(resultDictionary);
|
||||
}
|
||||
|
||||
public static TheoryData<IDictionary<long, int>> LongToIntData
|
||||
{
|
||||
get
|
||||
{
|
||||
var dictionaryWithOne = new Dictionary<long, int>
|
||||
{
|
||||
{ 0L, 0 },
|
||||
};
|
||||
var dictionaryWithThree = new Dictionary<long, int>
|
||||
{
|
||||
{ -1L, -1 },
|
||||
{ long.MaxValue, int.MaxValue },
|
||||
{ long.MinValue, int.MinValue },
|
||||
};
|
||||
|
||||
return new TheoryData<IDictionary<long, int>> { dictionaryWithOne, dictionaryWithThree };
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(LongToIntData))]
|
||||
public async Task BindModel_FallsBackToBindingValues_WithValueTypes(IDictionary<long, int> dictionary)
|
||||
{
|
||||
// Arrange
|
||||
var stringDictionary = dictionary.ToDictionary(kvp => kvp.Key.ToString(), kvp => kvp.Value.ToString());
|
||||
var binder = new DictionaryModelBinder<long, int>();
|
||||
var context = CreateContext();
|
||||
context.ModelName = "prefix";
|
||||
context.OperationBindingContext.ModelBinder = CreateCompositeBinder();
|
||||
context.OperationBindingContext.ValueProvider =
|
||||
CreateEnumerableValueProvider("prefix[{0}]", stringDictionary);
|
||||
context.ValueProvider = context.OperationBindingContext.ValueProvider;
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ModelWithDictionaryProperty),
|
||||
nameof(ModelWithDictionaryProperty.DictionaryProperty));
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.False(result.IsFatalError);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Equal("prefix", result.Key);
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
|
||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<long, int>>(result.Model);
|
||||
Assert.Equal(dictionary, resultDictionary);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindModel_FallsBackToBindingValues_WithComplexValues()
|
||||
{
|
||||
// Arrange
|
||||
var dictionary = new Dictionary<int, ModelWithProperties>
|
||||
{
|
||||
{ 23, new ModelWithProperties { Id = 43, Name = "Wilma" } },
|
||||
{ 27, new ModelWithProperties { Id = 98, Name = "Fred" } },
|
||||
};
|
||||
var stringDictionary = new Dictionary<string, string>
|
||||
{
|
||||
{ "prefix[23].Id", "43" },
|
||||
{ "prefix[23].Name", "Wilma" },
|
||||
{ "prefix[27].Id", "98" },
|
||||
{ "prefix[27].Name", "Fred" },
|
||||
};
|
||||
var binder = new DictionaryModelBinder<int, ModelWithProperties>();
|
||||
var context = CreateContext();
|
||||
context.ModelName = "prefix";
|
||||
context.OperationBindingContext.ModelBinder = CreateCompositeBinder();
|
||||
context.OperationBindingContext.ValueProvider = CreateEnumerableValueProvider("{0}", stringDictionary);
|
||||
context.ValueProvider = context.OperationBindingContext.ValueProvider;
|
||||
|
||||
var metadataProvider = context.OperationBindingContext.MetadataProvider;
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ModelWithDictionaryProperty),
|
||||
nameof(ModelWithDictionaryProperty.DictionaryProperty));
|
||||
|
||||
// Act
|
||||
var result = await binder.BindModelAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.False(result.IsFatalError);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Equal("prefix", result.Key);
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
|
||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<int, ModelWithProperties>>(result.Model);
|
||||
Assert.Equal(dictionary, resultDictionary);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DictionaryModelBinder_DoesNotCreateCollection_IfIsTopLevelObjectAndIsFirstChanceBinding()
|
||||
{
|
||||
|
|
@ -161,6 +358,46 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
return modelBindingContext;
|
||||
}
|
||||
|
||||
private static IModelBinder CreateCompositeBinder()
|
||||
{
|
||||
var binders = new IModelBinder[]
|
||||
{
|
||||
new TypeConverterModelBinder(),
|
||||
new TypeMatchModelBinder(),
|
||||
new MutableObjectModelBinder(),
|
||||
new ComplexModelDtoModelBinder(),
|
||||
};
|
||||
|
||||
return new CompositeModelBinder(binders);
|
||||
}
|
||||
|
||||
private static IValueProvider CreateEnumerableValueProvider(
|
||||
string keyFormat,
|
||||
IDictionary<string, string> dictionary)
|
||||
{
|
||||
// Convert to an IDictionary<string, string[]> then wrap it up.
|
||||
var backingStore = dictionary.ToDictionary(
|
||||
kvp => string.Format(keyFormat, kvp.Key),
|
||||
kvp => new[] { kvp.Value });
|
||||
var stringCollection = new ReadableStringCollection(backingStore);
|
||||
|
||||
return new ReadableStringCollectionValueProvider(
|
||||
BindingSource.Form,
|
||||
stringCollection,
|
||||
CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
// Like CreateEnumerableValueProvider except returned instance does not implement IEnumerableValueProvider.
|
||||
private static IValueProvider CreateTestValueProvider(string keyFormat, IDictionary<string, string> dictionary)
|
||||
{
|
||||
// Convert to an IDictionary<string, object> then wrap it up.
|
||||
var backingStore = dictionary.ToDictionary(
|
||||
kvp => string.Format(keyFormat, kvp.Key),
|
||||
kvp => (object)kvp.Value);
|
||||
|
||||
return new TestValueProvider(BindingSource.Form, backingStore);
|
||||
}
|
||||
|
||||
private static ModelBindingContext GetModelBindingContext(bool isReadOnly)
|
||||
{
|
||||
var metadataProvider = new TestModelMetadataProvider();
|
||||
|
|
@ -208,6 +445,32 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
public Dictionary<string, string> DictionaryProperty { get; set; }
|
||||
}
|
||||
|
||||
private class ModelWithProperties
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var other = obj as ModelWithProperties;
|
||||
return other != null &&
|
||||
Id == other.Id &&
|
||||
string.Equals(Name, other.Name, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int nameCode = Name == null ? 0 : Name.GetHashCode();
|
||||
return nameCode ^ Id.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{{ Id }, '{ Name }'}}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -16,10 +16,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
private static readonly IReadableStringCollection _backingStore = new ReadableStringCollection(
|
||||
new Dictionary<string, string[]>
|
||||
{
|
||||
{ "foo", new[] { "fooValue1", "fooValue2"} },
|
||||
{ "bar.baz", new[] {"someOtherValue" } },
|
||||
{ "some", new[] { "someValue1", "someValue2" } },
|
||||
{ "null_value", null },
|
||||
{ "prefix.null_value", null }
|
||||
{ "prefix.name", new[] { "someOtherValue" } },
|
||||
{ "prefix.null_value", null },
|
||||
{ "prefix.property1.property", null },
|
||||
{ "prefix.property2[index]", null },
|
||||
{ "prefix[index1]", null },
|
||||
{ "prefix[index1].property1", null },
|
||||
{ "prefix[index1].property2", null },
|
||||
{ "prefix[index2].property", null },
|
||||
{ "[index]", null },
|
||||
{ "[index].property", null },
|
||||
{ "[index][anotherIndex]", null },
|
||||
});
|
||||
|
||||
[Fact]
|
||||
|
|
@ -30,7 +39,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, backingStore, null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.ContainsPrefixAsync("");
|
||||
var result = await valueProvider.ContainsPrefixAsync(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
|
|
@ -43,7 +52,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.ContainsPrefixAsync("");
|
||||
var result = await valueProvider.ContainsPrefixAsync(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
|
|
@ -56,9 +65,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act & Assert
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("foo"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("bar"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("bar.baz"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("some"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("prefix"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("prefix.name"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("[index]"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("prefix[index1]"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -80,15 +91,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
// Arrange
|
||||
var expected = new Dictionary<string, string>
|
||||
{
|
||||
{ "bar", "bar" },
|
||||
{ "foo", "foo" },
|
||||
{ "index", "[index]" },
|
||||
{ "null_value", "null_value" },
|
||||
{ "prefix", "prefix" }
|
||||
{ "prefix", "prefix" },
|
||||
{ "some", "some" },
|
||||
};
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync("");
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, result.OrderBy(kvp => kvp.Key));
|
||||
|
|
@ -111,15 +122,40 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
public async Task GetKeysFromPrefixAsync_KnownPrefix_ReturnsMatchingItems()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new Dictionary<string, string>
|
||||
{
|
||||
{ "name", "prefix.name" },
|
||||
{ "null_value", "prefix.null_value" },
|
||||
{ "property1", "prefix.property1" },
|
||||
{ "property2", "prefix.property2" },
|
||||
{ "index1", "prefix[index1]" },
|
||||
{ "index2", "prefix[index2]" },
|
||||
};
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync("bar");
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync("prefix");
|
||||
|
||||
// Assert
|
||||
var kvp = Assert.Single(result);
|
||||
Assert.Equal("baz", kvp.Key);
|
||||
Assert.Equal("bar.baz", kvp.Value);
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetKeysFromPrefixAsync_IndexPrefix_ReturnsMatchingItems()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new Dictionary<string, string>
|
||||
{
|
||||
{ "property", "[index].property" },
|
||||
{ "anotherIndex", "[index][anotherIndex]" }
|
||||
};
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync("[index]");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -130,13 +166,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, culture);
|
||||
|
||||
// Act
|
||||
var vpResult = await valueProvider.GetValueAsync("bar.baz");
|
||||
var result = await valueProvider.GetValueAsync("prefix.name");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(vpResult);
|
||||
Assert.Equal("someOtherValue", vpResult.RawValue);
|
||||
Assert.Equal("someOtherValue", vpResult.AttemptedValue);
|
||||
Assert.Equal(culture, vpResult.Culture);
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("someOtherValue", result.RawValue);
|
||||
Assert.Equal("someOtherValue", result.AttemptedValue);
|
||||
Assert.Equal(culture, result.Culture);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -147,13 +183,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, culture);
|
||||
|
||||
// Act
|
||||
var vpResult = await valueProvider.GetValueAsync("foo");
|
||||
var result = await valueProvider.GetValueAsync("some");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(vpResult);
|
||||
Assert.Equal(new[] { "fooValue1", "fooValue2" }, (IList<string>)vpResult.RawValue);
|
||||
Assert.Equal("fooValue1,fooValue2", vpResult.AttemptedValue);
|
||||
Assert.Equal(culture, vpResult.Culture);
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(new[] { "someValue1", "someValue2" }, (IList<string>)result.RawValue);
|
||||
Assert.Equal("someValue1,someValue2", result.AttemptedValue);
|
||||
Assert.Equal(culture, result.Culture);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -185,11 +221,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, backingStore, culture);
|
||||
|
||||
// Act
|
||||
var vpResult = await valueProvider.GetValueAsync("key");
|
||||
var result = await valueProvider.GetValueAsync("key");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { null, null, "value" }, vpResult.RawValue as IEnumerable<string>);
|
||||
Assert.Equal(",,value", vpResult.AttemptedValue);
|
||||
Assert.Equal(new[] { null, null, "value" }, result.RawValue as IEnumerable<string>);
|
||||
Assert.Equal(",,value", result.AttemptedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -199,10 +235,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act
|
||||
var vpResult = await valueProvider.GetValueAsync("bar");
|
||||
var result = await valueProvider.GetValueAsync("prefix");
|
||||
|
||||
// Assert
|
||||
Assert.Null(vpResult);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -101,13 +101,19 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
// Arrange
|
||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||
var client = server.CreateClient();
|
||||
var expected = "6 feet";
|
||||
|
||||
// Act
|
||||
var expression = await client.GetStringAsync("http://localhost/RoundTrip/GetPersonParentHeightAttribute");
|
||||
var keyValuePairs = new[]
|
||||
{
|
||||
new KeyValuePair<string, string>(expression, expected),
|
||||
};
|
||||
var result = await GetPerson(client, keyValuePairs);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Parent.Attributes[height]", expression);
|
||||
// TODO: https://github.com/aspnet/Mvc/issues/1418 Requires resolution in model binding
|
||||
Assert.Equal(expected, result.Parent.Attributes["height"]);
|
||||
}
|
||||
|
||||
// Uses the expression p => p.Dependents[0].Dependents[0].Name
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// 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;
|
||||
|
|
@ -14,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
public class DictionaryModelBinderIntegrationTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfSimpleType_WithPrefix_Success()
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfSimpleType_WithPrefixAndKVP_Success()
|
||||
{
|
||||
// Arrange
|
||||
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
|
||||
|
|
@ -55,7 +54,48 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfSimpleType_WithExplicitPrefix_Success()
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfSimpleType_WithPrefixAndItem_Success()
|
||||
{
|
||||
// Arrange
|
||||
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
|
||||
var parameter = new ParameterDescriptor()
|
||||
{
|
||||
Name = "parameter",
|
||||
ParameterType = typeof(Dictionary<string, int>)
|
||||
};
|
||||
|
||||
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
|
||||
{
|
||||
request.QueryString = new QueryString("?parameter[key0]=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<Dictionary<string, int>>(modelBindingResult.Model);
|
||||
Assert.Equal(new Dictionary<string, int>() { { "key0", 10 } }, model);
|
||||
|
||||
Assert.Equal(0, modelState.ErrorCount);
|
||||
Assert.True(modelState.IsValid);
|
||||
|
||||
var kvp = Assert.Single(modelState);
|
||||
Assert.Equal("parameter[key0]", kvp.Key);
|
||||
var entry = kvp.Value;
|
||||
Assert.Equal("10", entry.Value.AttemptedValue);
|
||||
Assert.Equal("10", entry.Value.RawValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("?prefix[key0]=10")]
|
||||
[InlineData("?prefix[0].Key=key0&prefix[0].Value=10")]
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfSimpleType_WithExplicitPrefix_Success(
|
||||
string queryString)
|
||||
{
|
||||
// Arrange
|
||||
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
|
||||
|
|
@ -71,7 +111,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
|
||||
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
|
||||
{
|
||||
request.QueryString = new QueryString("?prefix[0].Key=key0&prefix[0].Value=10");
|
||||
request.QueryString = new QueryString(queryString);
|
||||
});
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -86,21 +126,15 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
var model = Assert.IsType<Dictionary<string, int>>(modelBindingResult.Model);
|
||||
Assert.Equal(new Dictionary<string, int>() { { "key0", 10 }, }, model);
|
||||
|
||||
Assert.Equal(2, modelState.Count);
|
||||
Assert.NotEmpty(modelState);
|
||||
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").Value;
|
||||
Assert.Equal("10", entry.Value.AttemptedValue);
|
||||
Assert.Equal("10", entry.Value.RawValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfSimpleType_EmptyPrefix_Success()
|
||||
[Theory]
|
||||
[InlineData("?[key0]=10")]
|
||||
[InlineData("?[0].Key=key0&[0].Value=10")]
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfSimpleType_EmptyPrefix_Success(string queryString)
|
||||
{
|
||||
// Arrange
|
||||
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
|
||||
|
|
@ -112,7 +146,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
|
||||
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
|
||||
{
|
||||
request.QueryString = new QueryString("?[0].Key=key0&[0].Value=10");
|
||||
request.QueryString = new QueryString(queryString);
|
||||
});
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -127,17 +161,9 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
var model = Assert.IsType<Dictionary<string, int>>(modelBindingResult.Model);
|
||||
Assert.Equal(new Dictionary<string, int>() { { "key0", 10 }, }, model);
|
||||
|
||||
Assert.Equal(2, modelState.Count);
|
||||
Assert.NotEmpty(modelState);
|
||||
Assert.Equal(0, modelState.ErrorCount);
|
||||
Assert.True(modelState.IsValid);
|
||||
|
||||
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").Value;
|
||||
Assert.Equal("10", entry.Value.AttemptedValue);
|
||||
Assert.Equal("10", entry.Value.RawValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -164,9 +190,11 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
// Assert
|
||||
Assert.NotNull(modelBindingResult);
|
||||
Assert.True(modelBindingResult.IsModelSet);
|
||||
Assert.Empty(Assert.IsType<Dictionary<string, int>>(modelBindingResult.Model));
|
||||
|
||||
Assert.Equal(0, modelState.Count);
|
||||
var model = Assert.IsType<Dictionary<string, int>>(modelBindingResult.Model);
|
||||
Assert.Empty(model);
|
||||
|
||||
Assert.Empty(modelState);
|
||||
Assert.Equal(0, modelState.ErrorCount);
|
||||
Assert.True(modelState.IsValid);
|
||||
}
|
||||
|
|
@ -174,10 +202,29 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
private class Person
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var other = obj as Person;
|
||||
|
||||
return other != null && Id == other.Id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ { Id } }}";
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfComplexType_WithPrefix_Success()
|
||||
[Theory]
|
||||
[InlineData("?parameter[key0].Id=10")]
|
||||
[InlineData("?parameter[0].Key=key0¶meter[0].Value.Id=10")]
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfComplexType_WithPrefix_Success(string queryString)
|
||||
{
|
||||
// Arrange
|
||||
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
|
||||
|
|
@ -189,7 +236,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
|
||||
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
|
||||
{
|
||||
request.QueryString = new QueryString("?parameter[0].Key=key0¶meter[0].Value.Id=10");
|
||||
request.QueryString = new QueryString(queryString);
|
||||
});
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -202,25 +249,18 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
Assert.True(modelBindingResult.IsModelSet);
|
||||
|
||||
var model = Assert.IsType<Dictionary<string, Person>>(modelBindingResult.Model);
|
||||
Assert.Equal(1, model.Count);
|
||||
Assert.Equal("key0", model.Keys.First());
|
||||
Assert.Equal(model.Values, model.Values);
|
||||
Assert.Equal(new Dictionary<string, Person> { { "key0", new Person { Id = 10 } }, }, model);
|
||||
|
||||
Assert.Equal(2, modelState.Count);
|
||||
Assert.NotEmpty(modelState);
|
||||
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]
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfComplexType_WithExplicitPrefix_Success()
|
||||
[Theory]
|
||||
[InlineData("?prefix[key0].Id=10")]
|
||||
[InlineData("?prefix[0].Key=key0&prefix[0].Value.Id=10")]
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfComplexType_WithExplicitPrefix_Success(
|
||||
string queryString)
|
||||
{
|
||||
// Arrange
|
||||
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
|
||||
|
|
@ -236,7 +276,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
|
||||
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
|
||||
{
|
||||
request.QueryString = new QueryString("?prefix[0].Key=key0&prefix[0].Value.Id=10");
|
||||
request.QueryString = new QueryString(queryString);
|
||||
});
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -249,25 +289,17 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
Assert.True(modelBindingResult.IsModelSet);
|
||||
|
||||
var model = Assert.IsType<Dictionary<string, Person>>(modelBindingResult.Model);
|
||||
Assert.Equal(1, model.Count);
|
||||
Assert.Equal("key0", model.Keys.First());
|
||||
Assert.Equal(model.Values, model.Values);
|
||||
Assert.Equal(new Dictionary<string, Person> { { "key0", new Person { Id = 10 } }, }, model);
|
||||
|
||||
Assert.Equal(2, modelState.Count);
|
||||
Assert.NotEmpty(modelState);
|
||||
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]
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfComplexType_EmptyPrefix_Success()
|
||||
[Theory]
|
||||
[InlineData("?[key0].Id=10")]
|
||||
[InlineData("?[0].Key=key0&[0].Value.Id=10")]
|
||||
public async Task DictionaryModelBinder_BindsDictionaryOfComplexType_EmptyPrefix_Success(string queryString)
|
||||
{
|
||||
// Arrange
|
||||
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
|
||||
|
|
@ -279,7 +311,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
|
||||
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
|
||||
{
|
||||
request.QueryString = new QueryString("?[0].Key=key0&[0].Value.Id=10");
|
||||
request.QueryString = new QueryString(queryString);
|
||||
});
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -292,21 +324,11 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
Assert.True(modelBindingResult.IsModelSet);
|
||||
|
||||
var model = Assert.IsType<Dictionary<string, Person>>(modelBindingResult.Model);
|
||||
Assert.Equal(1, model.Count);
|
||||
Assert.Equal("key0", model.Keys.First());
|
||||
Assert.Equal(model.Values, model.Values);
|
||||
Assert.Equal(new Dictionary<string, Person> { { "key0", new Person { Id = 10 } }, }, model);
|
||||
|
||||
Assert.Equal(2, modelState.Count);
|
||||
Assert.NotEmpty(modelState);
|
||||
Assert.Equal(0, modelState.ErrorCount);
|
||||
Assert.True(modelState.IsValid);
|
||||
|
||||
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]
|
||||
|
|
@ -333,9 +355,11 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
// Assert
|
||||
Assert.NotNull(modelBindingResult);
|
||||
Assert.True(modelBindingResult.IsModelSet);
|
||||
Assert.Empty(Assert.IsType<Dictionary<string, Person>>(modelBindingResult.Model));
|
||||
|
||||
Assert.Equal(0, modelState.Count);
|
||||
var model = Assert.IsType<Dictionary<string, Person>>(modelBindingResult.Model);
|
||||
Assert.Empty(model);
|
||||
|
||||
Assert.Empty(modelState);
|
||||
Assert.Equal(0, modelState.ErrorCount);
|
||||
Assert.True(modelState.IsValid);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue