diff --git a/NuGet.Config b/NuGet.Config index 56d86acac0..b20b7336fc 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -1,4 +1,4 @@ - + diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelState.cs b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelState.cs index 4a22ccc444..213b8b6922 100644 --- a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelState.cs +++ b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelState.cs @@ -3,17 +3,29 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { + /// + /// An entry in a . + /// public class ModelState { - private readonly ModelErrorCollection _errors = new ModelErrorCollection(); + /// + /// Gets the raw value from the request associated with this entry. + /// + public object RawValue { get; set; } - public ValueProviderResult Value { get; set; } + /// + /// Gets the set of values contained in , joined into a comma-separated string. + /// + public string AttemptedValue { get; set; } - public ModelErrorCollection Errors - { - get { return _errors; } - } + /// + /// Gets the for this entry. + /// + public ModelErrorCollection Errors { get; } = new ModelErrorCollection(); + /// + /// Gets or sets the for this entry. + /// public ModelValidationState ValidationState { get; set; } } } diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelStateDictionary.cs b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelStateDictionary.cs index abca9b58a5..e604b9d634 100644 --- a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelStateDictionary.cs +++ b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelStateDictionary.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Microsoft.AspNet.Mvc.Abstractions; using Microsoft.Framework.Internal; @@ -213,6 +214,7 @@ public bool TryAddModelError([NotNull] string key, [NotNull] Exception exception // Convert FormatExceptions to Invalid value messages. ModelState modelState; TryGetValue(key, out modelState); + string errorMessage; if (modelState == null) { @@ -221,7 +223,7 @@ public bool TryAddModelError([NotNull] string key, [NotNull] Exception exception else { errorMessage = Resources.FormatModelError_InvalidValue_MessageWithModelValue( - modelState.Value.AttemptedValue, + modelState.AttemptedValue, key); } @@ -359,14 +361,50 @@ public void Merge(ModelStateDictionary dictionary) } /// - /// Sets the value for the with the specified to the - /// specified . + /// Sets the of and for + /// the with the specified . /// /// The key for the entry. - /// The value to assign. - public void SetModelValue([NotNull] string key, [NotNull] ValueProviderResult value) + /// The raw value for the entry. + /// + /// The values of in a comma-separated . + /// + public void SetModelValue([NotNull] string key, object rawValue, string attemptedValue) { - GetModelStateForKey(key).Value = value; + var modelState = GetModelStateForKey(key); + modelState.RawValue = rawValue; + modelState.AttemptedValue = attemptedValue; + } + + /// + /// Sets the value for the with the specified . + /// + /// The key for the entry + /// + /// A with data for the entry. + /// + public void SetModelValue([NotNull] string key, ValueProviderResult valueProviderResult) + { + // Avoid creating a new array for rawvalue if there's only one value. + object rawValue; + if (valueProviderResult == ValueProviderResult.None) + { + rawValue = null; + } + else if (valueProviderResult.Value != null) + { + rawValue = valueProviderResult.Value; + } + else if (valueProviderResult.Length == 1) + { + rawValue = valueProviderResult.Values[0]; + } + else + { + rawValue = valueProviderResult.Values; + } + + SetModelValue(key, rawValue, (string)valueProviderResult); } /// diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ValueProviderResult.cs b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ValueProviderResult.cs index 04f6dcf1ba..1c92b91ad8 100644 --- a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ValueProviderResult.cs +++ b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ValueProviderResult.cs @@ -3,227 +3,353 @@ using System; using System.Collections; -using System.ComponentModel; +using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; -using System.Reflection; -using System.Runtime.ExceptionServices; -using Microsoft.AspNet.Mvc.Abstractions; using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Mvc.ModelBinding { /// - /// Result of an operation. + /// Result of an operation. /// - public class ValueProviderResult + /// + /// + /// can represent a single submitted value or multiple submitted values. + /// + /// + /// Use to consume only a single value, regardless of whether a single value or + /// multiple values were submitted. + /// + /// + /// Treat as an to consume all values, + /// regardless of whether a single value or multiple values were submitted. + /// + /// + public struct ValueProviderResult : IEquatable, IEnumerable { - private static readonly CultureInfo _staticCulture = CultureInfo.InvariantCulture; + private static readonly CultureInfo _invariantCulture = CultureInfo.InvariantCulture; /// - /// Instantiates a new instance of the class with given - /// . Initializes to - /// . + /// A that represents a lack of data. /// - /// The value of the new instance. - public ValueProviderResult(object rawValue) - : this(rawValue, attemptedValue: null, culture: _staticCulture) - { - } + public static ValueProviderResult None = new ValueProviderResult(new string[0]); /// - /// Instantiates a new instance of the class with given - /// , , and . + /// Creates a new using . /// - /// The value of the new instance. - /// The value of the new instance. - /// The value of the new instance. - public ValueProviderResult(object rawValue, string attemptedValue, CultureInfo culture) + /// The submitted value. + public ValueProviderResult(string value) + : this(value, _invariantCulture) { - RawValue = rawValue; - AttemptedValue = attemptedValue; - Culture = culture ?? _staticCulture; } /// - /// conversion of . - /// - /// - /// Used in helpers that generate <textarea> elements as well as some error messages. - /// - public string AttemptedValue { get; } - - /// - /// to use in or - /// if passed is null. + /// Creates a new . /// - public CultureInfo Culture { get; } + /// The submitted value. + /// The associated with this value. + public ValueProviderResult(string value, CultureInfo culture) + { + if (value == null) + { + Value = null; + Values = None.Values; + } + else + { + Value = value; + Values = null; + } - /// - /// The provided . - /// - public object RawValue { get; } + Culture = culture ?? _invariantCulture; + } /// - /// Converts to the given . Uses for - /// operations. + /// Creates a new using . /// - /// The target of the conversion. - /// - /// converted to the given . null if the conversion fails. - /// - public object ConvertTo(Type type) + /// The submitted values. + public ValueProviderResult(string[] values) + : this(values, _invariantCulture) { - return ConvertTo(type, culture: null); } /// - /// Converts to the given using the given - /// . + /// Creates a new . /// - /// The target of the conversion. - /// - /// The to use for operations. Uses - /// if this parameter is null. - /// - /// - /// converted to the given using the given - /// . null if the conversion fails. - /// - public virtual object ConvertTo([NotNull] Type type, CultureInfo culture) + /// The submitted values. + /// The associated with these values. + public ValueProviderResult(string[] values, CultureInfo culture) { - var value = RawValue; - if (value == null) + if (values == null) { - // treat null route parameters as though they were the default value for the type - return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : - null; + Value = null; + Values = None.Values; } - - if (value.GetType().IsAssignableFrom(type)) + else { - return value; + Value = null; + Values = values; } - var cultureToUse = culture ?? Culture; - return UnwrapPossibleArrayType(cultureToUse, value, type); + Culture = culture; } - private object UnwrapPossibleArrayType(CultureInfo culture, object value, Type destinationType) + /// + /// Gets or sets the associated with the values. + /// + public CultureInfo Culture { get; private set; } + + /// + /// Gets or sets a single value. Will be null if multiple values are present. + /// + public string Value { get; private set; } + + /// + /// Gets or sets an array of values. Will be null if only a single value was provided. + /// + public string[] Values { get; private set; } + + /// + /// Gets the first value based on the order values were provided in the request. Use + /// to get a single value for processing regarless of whether a single or multiple values were provided + /// in the request. + /// + public string FirstValue { - // array conversion results in four cases, as below - var valueAsArray = value as Array; - if (destinationType.IsArray) + get { - var destinationElementType = destinationType.GetElementType(); - if (valueAsArray != null) + if (Value != null) { - // case 1: both destination + source type are arrays, so convert each element - var converted = (IList)Array.CreateInstance(destinationElementType, valueAsArray.Length); - for (var i = 0; i < valueAsArray.Length; i++) - { - converted[i] = ConvertSimpleType(culture, valueAsArray.GetValue(i), destinationElementType); - } - return converted; + return Value; + } + else if (Values != null && Values.Length > 0) + { + return Values[0]; } else { - // case 2: destination type is array but source is single element, so wrap element in - // array + convert - var element = ConvertSimpleType(culture, value, destinationElementType); - var converted = (IList)Array.CreateInstance(destinationElementType, 1); - converted[0] = element; - return converted; + return null; } } - else if (valueAsArray != null) + } + + /// + /// Gets the number of submitted values. + /// + public int Length + { + get { - // case 3: destination type is single element but source is array, so extract first element + convert - if (valueAsArray.Length > 0) + if (Values != null) { - value = valueAsArray.GetValue(0); - return ConvertSimpleType(culture, value, destinationType); + return Values.Length; + } + else if (Value != null) + { + return 1; } else { - // case 3(a): source is empty array, so can't perform conversion - return null; + return 0; } } + } - // case 4: both destination + source type are single elements, so convert - return ConvertSimpleType(culture, value, destinationType); + /// + public override bool Equals(object obj) + { + var other = obj as ValueProviderResult?; + return other.HasValue ? Equals(other.Value) : false; } - private object ConvertSimpleType(CultureInfo culture, object value, Type destinationType) + /// + public bool Equals(ValueProviderResult other) { - if (value == null || value.GetType().IsAssignableFrom(destinationType)) + if (Length != other.Length) { - return value; + return false; } + else + { + var x = (string[])this; + var y = (string[])other; + for (var i = 0; i < x.Length; i++) + { + if (!string.Equals(x[i], y[i], StringComparison.Ordinal)) + { + return false; + } + } + return true; + } + } + + /// + public override int GetHashCode() + { + return ((string)this)?.GetHashCode() ?? 0; + } - // In case of a Nullable object, we try again with its underlying type. - destinationType = UnwrapNullableType(destinationType); + /// + public override string ToString() + { + return (string)this; + } - // if this is a user-input value but the user didn't type anything, return no value - var valueAsString = value as string; - if (valueAsString != null && string.IsNullOrWhiteSpace(valueAsString)) + /// + /// Gets an for this . + /// + /// An . + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Converts the provided into a comma-separated string containing all + /// submitted values. + /// + /// The . + public static explicit operator string(ValueProviderResult result) + { + if (result.Values == null) + { + return result.Value; + } + else if (result.Values.Length == 0) { return null; } + else if (result.Values.Length == 1) + { + return result.Values[0]; + } + else + { + return string.Join(",", result.Values); + } + } - var converter = TypeDescriptor.GetConverter(destinationType); - var canConvertFrom = converter.CanConvertFrom(value.GetType()); - if (!canConvertFrom) + /// + /// Converts the provided into a an array of containing + /// all submitted values. + /// + /// The . + public static explicit operator string[](ValueProviderResult result) + { + if (result.Values != null) { - converter = TypeDescriptor.GetConverter(value.GetType()); + return result.Values; } - if (!(canConvertFrom || converter.CanConvertTo(destinationType))) + else if (result.Value != null) { - // EnumConverter cannot convert integer, so we verify manually - if (destinationType.GetTypeInfo().IsEnum && - (value is int || - value is uint || - value is long || - value is ulong || - value is short || - value is ushort || - value is byte || - value is sbyte)) - { - return Enum.ToObject(destinationType, value); - } + return new string[] { result.Value }; + } + else + { + return None.Values; + } + } - throw new InvalidOperationException( - Resources.FormatValueProviderResult_NoConverterExists(value.GetType(), destinationType)); + /// + /// Compares two objects for equality. + /// + /// A . + /// A . + /// true if the values are equal, otherwise false. + public static bool operator ==(ValueProviderResult x, ValueProviderResult y) + { + return x.Equals(y); + } + + /// + /// Compares two objects for inequality. + /// + /// A . + /// A . + /// false if the values are equal, otherwise true. + public static bool operator !=(ValueProviderResult x, ValueProviderResult y) + { + return !x.Equals(y); + } + + /// + /// An enumerator for . + /// + public struct Enumerator : IEnumerator + { + private readonly ValueProviderResult _result; + private readonly int _length; + private int _index; + + /// + /// Creates a new . + /// + /// The . + public Enumerator(ValueProviderResult result) + { + _result = result; + _index = -1; + _length = result.Length; + Current = null; } - try + /// + public string Current { get; private set; } + + /// + object IEnumerator.Current => Current; + + /// + public void Dispose() { - return canConvertFrom - ? converter.ConvertFrom(null, culture, value) - : converter.ConvertTo(null, culture, value, destinationType); } - catch (Exception ex) + + /// + public bool MoveNext() { - if (ex is FormatException) + ++_index; + if (_index < _length) { - throw ex; + if (_result.Values != null) + { + Debug.Assert(_index < _result.Values.Length); + Current = _result.Values[_index]; + return true; + } + else if (_result.Value != null && _index == 0) + { + Current = _result.Value; + return true; + } + else + { + return false; + } } - else - { - // TypeConverter throws System.Exception wrapping the FormatException, - // so we throw the inner exception. - ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); - // this code is never reached because the previous line is throwing; - throw; - } + Current = null; + return false; } - } - private static Type UnwrapNullableType(Type destinationType) - { - return Nullable.GetUnderlyingType(destinationType) ?? destinationType; + /// + public void Reset() + { + _index = -1; + } } } } diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Abstractions/Properties/Resources.Designer.cs index 21f45e4748..3b4ee1a724 100644 --- a/src/Microsoft.AspNet.Mvc.Abstractions/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.Abstractions/Properties/Resources.Designer.cs @@ -74,38 +74,6 @@ internal static string FormatValidation_InvalidFieldCannotBeReset_ToSkipped() return GetString("Validation_InvalidFieldCannotBeReset_ToSkipped"); } - /// - /// Cannot convert value '{0}' to enum type '{1}'. - /// - internal static string ValueProviderResult_CannotConvertEnum - { - get { return GetString("ValueProviderResult_CannotConvertEnum"); } - } - - /// - /// Cannot convert value '{0}' to enum type '{1}'. - /// - internal static string FormatValueProviderResult_CannotConvertEnum(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ValueProviderResult_CannotConvertEnum"), p0, p1); - } - - /// - /// The parameter conversion from type '{0}' to type '{1}' failed because no type converter can convert between these types. - /// - internal static string ValueProviderResult_NoConverterExists - { - get { return GetString("ValueProviderResult_NoConverterExists"); } - } - - /// - /// The parameter conversion from type '{0}' to type '{1}' failed because no type converter can convert between these types. - /// - internal static string FormatValueProviderResult_NoConverterExists(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ValueProviderResult_NoConverterExists"), p0, p1); - } - /// /// The maximum number of allowed model errors has been reached. /// diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/Resources.resx b/src/Microsoft.AspNet.Mvc.Abstractions/Resources.resx index f7d54060f6..d9ad44cf72 100644 --- a/src/Microsoft.AspNet.Mvc.Abstractions/Resources.resx +++ b/src/Microsoft.AspNet.Mvc.Abstractions/Resources.resx @@ -129,12 +129,6 @@ A field previously marked invalid should not be marked skipped. - - Cannot convert value '{0}' to enum type '{1}'. - - - The parameter conversion from type '{0}' to type '{1}' failed because no type converter can convert between these types. - The maximum number of allowed model errors has been reached. diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/BodyModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/BodyModelBinder.cs index 07ceecff7e..325aedd2bc 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/BodyModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/BodyModelBinder.cs @@ -57,6 +57,8 @@ protected async override Task BindModelCoreAsync( { var previousCount = bindingContext.ModelState.ErrorCount; var model = await formatter.ReadAsync(formatterContext); + + bindingContext.ModelState.SetModelValue(modelBindingKey, rawValue: model, attemptedValue: null); if (bindingContext.ModelState.ErrorCount != previousCount) { @@ -65,9 +67,6 @@ protected async override Task BindModelCoreAsync( return new ModelBindingResult(modelBindingKey); } - var valueProviderResult = new ValueProviderResult(rawValue: model); - bindingContext.ModelState.SetModelValue(modelBindingKey, valueProviderResult); - var validationNode = new ModelValidationNode(modelBindingKey, bindingContext.ModelMetadata, model) { ValidateAllProperties = true diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs index 17cd3d5845..c49f1fcdde 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs @@ -23,7 +23,7 @@ public async Task BindModelAsync([NotNull] ModelBindingConte // Check for missing data case 1: There was no element containing this data. var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); - if (valueProviderResult == null) + if (valueProviderResult == ValueProviderResult.None) { return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); } @@ -31,7 +31,7 @@ public async Task BindModelAsync([NotNull] ModelBindingConte bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); // Check for missing data case 2: There was an element but it was left blank. - var value = valueProviderResult.AttemptedValue; + var value = valueProviderResult.FirstValue; if (string.IsNullOrEmpty(value)) { return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CollectionModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CollectionModelBinder.cs index c0074c1f2e..6cc2e1b291 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CollectionModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CollectionModelBinder.cs @@ -56,31 +56,13 @@ public virtual async Task BindModelAsync([NotNull] ModelBind var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); CollectionResult result; - if (valueProviderResult == null) + if (valueProviderResult == ValueProviderResult.None) { result = await BindComplexCollection(bindingContext); } else { - if (valueProviderResult.RawValue == null) - { - // Value exists but is null. Handle similarly to fallback case above. This avoids a - // ModelBindingResult with IsModelSet = true but ValidationNode = null. - model = bindingContext.Model ?? CreateEmptyCollection(bindingContext.ModelType); - var validationNode = - new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model); - - return new ModelBindingResult( - model, - bindingContext.ModelName, - isModelSet: true, - validationNode: validationNode); - } - - result = await BindSimpleCollection( - bindingContext, - valueProviderResult.RawValue, - valueProviderResult.Culture); + result = await BindSimpleCollection(bindingContext, valueProviderResult); } var boundCollection = result.Model; @@ -94,6 +76,15 @@ public virtual async Task BindModelAsync([NotNull] ModelBind CopyToModel(model, boundCollection); } + if (valueProviderResult != ValueProviderResult.None) + { + // If we did simple binding, then modelstate should be updated to reflect what we bound for ModelName. + // If we did complex binding, there will already be an entry for each index. + bindingContext.ModelState.SetModelValue( + bindingContext.ModelName, + valueProviderResult); + } + return new ModelBindingResult( model, bindingContext.ModelName, @@ -147,8 +138,7 @@ protected object CreateInstance(Type targetType) // Internal for testing. internal async Task BindSimpleCollection( ModelBindingContext bindingContext, - object rawValue, - CultureInfo culture) + ValueProviderResult values) { var boundCollection = new List(); @@ -159,8 +149,7 @@ internal async Task BindSimpleCollection( bindingContext.ModelName, bindingContext.ModelMetadata, boundCollection); - var rawValueArray = RawValueToObjectArray(rawValue); - foreach (var rawValueElement in rawValueArray) + foreach (var value in values) { var innerBindingContext = ModelBindingContext.GetChildModelBindingContext( bindingContext, @@ -169,7 +158,7 @@ internal async Task BindSimpleCollection( innerBindingContext.ValueProvider = new CompositeValueProvider { // our temporary provider goes at the front of the list - new ElementalValueProvider(bindingContext.ModelName, rawValueElement, culture), + new ElementalValueProvider(bindingContext.ModelName, value, values.Culture), bindingContext.ValueProvider }; @@ -183,8 +172,9 @@ internal async Task BindSimpleCollection( { validationNode.ChildNodes.Add(result.ValidationNode); } + + boundCollection.Add(ModelBindingHelper.CastOrDefault(boundValue)); } - boundCollection.Add(ModelBindingHelper.CastOrDefault(boundValue)); } return new CollectionResult @@ -364,7 +354,7 @@ private static IEnumerable GetIndexNamesFromValueProviderResult(ValuePro IEnumerable indexNames = null; if (valueProviderResult != null) { - var indexes = (string[])valueProviderResult.ConvertTo(typeof(string[])); + var indexes = (string[])valueProviderResult; if (indexes != null && indexes.Length > 0) { indexNames = indexes; diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CompositeValueProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CompositeValueProvider.cs index fdbaff8e59..65ef7c6ec5 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CompositeValueProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CompositeValueProvider.cs @@ -86,12 +86,13 @@ public virtual async Task GetValueAsync(string key) { var valueProvider = Items[i]; var result = await valueProvider.GetValueAsync(key); - if (result != null) + if (result != ValueProviderResult.None) { return result; } } - return null; + + return ValueProviderResult.None; } /// diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryBasedValueProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryBasedValueProvider.cs index fec85a4025..39e9547f1b 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryBasedValueProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryBasedValueProvider.cs @@ -30,21 +30,23 @@ public DictionaryBasedValueProvider( _values = values; } - /// - public override Task ContainsPrefixAsync(string key) - { - var prefixContainer = GetOrCreatePrefixContainer(); - return Task.FromResult(prefixContainer.ContainsPrefix(key)); - } - - private PrefixContainer GetOrCreatePrefixContainer() + protected PrefixContainer PrefixContainer { - if (_prefixContainer == null) + get { - _prefixContainer = new PrefixContainer(_values.Keys); + if (_prefixContainer == null) + { + _prefixContainer = new PrefixContainer(_values.Keys); + } + + return _prefixContainer; } + } - return _prefixContainer; + /// + public override Task ContainsPrefixAsync(string key) + { + return Task.FromResult(PrefixContainer.ContainsPrefix(key)); } /// @@ -54,12 +56,12 @@ public override Task GetValueAsync([NotNull] string key) ValueProviderResult result; if (_values.TryGetValue(key, out value)) { - var attemptedValue = value != null ? value.ToString() : null; - result = new ValueProviderResult(value, attemptedValue, CultureInfo.InvariantCulture); + var stringValue = value as string ?? value?.ToString() ?? string.Empty; + result = new ValueProviderResult(stringValue, CultureInfo.InvariantCulture); } else { - result = null; + result = ValueProviderResult.None; } return Task.FromResult(result); diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryModelBinder.cs index 3990f3255f..9d3fac8d4c 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/DictionaryModelBinder.cs @@ -65,15 +65,18 @@ public override async Task BindModelAsync([NotNull] ModelBin var modelBinder = bindingContext.OperationBindingContext.ModelBinder; var validationNode = result.ValidationNode; - foreach (var key in keys) + foreach (var kvp in keys) { - var dictionaryKey = ConvertFromString(key.Key); - valueBindingContext.ModelName = key.Value; + // Use InvariantCulture to convert the key since ExpressionHelper.GetExpressionText() would use + // that culture when rendering a form. + var convertedKey = ModelBindingHelper.ConvertTo(kvp.Key, culture: null); + + valueBindingContext.ModelName = kvp.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(valueResult?.Model); + model[convertedKey] = ModelBindingHelper.CastOrDefault(valueResult?.Model); if (valueResult != null && valueResult.IsModelSet) { validationNode.ChildNodes.Add(valueResult.ValidationNode); @@ -116,14 +119,5 @@ protected override object CreateEmptyCollection(Type targetType) return CreateInstance(targetType); } - - 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(keyObject); - } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ElementalValueProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ElementalValueProvider.cs index f64dd4263d..25e7f96aee 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ElementalValueProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ElementalValueProvider.cs @@ -7,33 +7,36 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { - // Represents a value provider that contains a single value. - internal sealed class ElementalValueProvider : IValueProvider + public class ElementalValueProvider : IValueProvider { - public ElementalValueProvider(string name, object rawValue, CultureInfo culture) + public ElementalValueProvider(string key, string value, CultureInfo culture) { - Name = name; - RawValue = rawValue; + Key = key; + Value = value; Culture = culture; } - public CultureInfo Culture { get; private set; } + public CultureInfo Culture { get; } - public string Name { get; private set; } + public string Key { get; } - public object RawValue { get; private set; } + public string Value { get; } public Task ContainsPrefixAsync(string prefix) { - return Task.FromResult(PrefixContainer.IsPrefixMatch(prefix, Name)); + return Task.FromResult(PrefixContainer.IsPrefixMatch(prefix, Key)); } public Task GetValueAsync(string key) { - var result = string.Equals(key, Name, StringComparison.OrdinalIgnoreCase) ? - new ValueProviderResult(RawValue, Convert.ToString(RawValue, Culture), Culture) : - null; - return Task.FromResult(result); + if (string.Equals(key, Key, StringComparison.OrdinalIgnoreCase)) + { + return Task.FromResult(new ValueProviderResult(Value, Culture)); + } + else + { + return Task.FromResult(ValueProviderResult.None); + } } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormFileModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormFileModelBinder.cs index de994ecbad..ef9b8ef5b9 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormFileModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormFileModelBinder.cs @@ -48,8 +48,10 @@ public async Task BindModelAsync([NotNull] ModelBindingConte SuppressValidation = true, }; - var valueProviderResult = new ValueProviderResult(rawValue: value); - bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); + bindingContext.ModelState.SetModelValue( + bindingContext.ModelName, + rawValue: null, + attemptedValue: null); } return new ModelBindingResult( diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/HeaderModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/HeaderModelBinder.cs index 2a253ece64..a7b5cbb1a9 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/HeaderModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/HeaderModelBinder.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; -using System.Globalization; +using System.Linq; #if DNXCORE50 using System.Reflection; #endif @@ -60,10 +60,11 @@ protected override Task BindModelCoreAsync([NotNull] ModelBi bindingContext.ModelName, bindingContext.ModelMetadata, model); - - var attemptedValue = (model as string) ?? request.Headers.Get(headerName); - var valueProviderResult = new ValueProviderResult(model, attemptedValue, CultureInfo.InvariantCulture); - bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); + + bindingContext.ModelState.SetModelValue( + bindingContext.ModelName, + request.Headers.GetCommaSeparatedValues(headerName).ToArray(), + request.Headers.Get(headerName)); } return Task.FromResult( diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/JQueryFormValueProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/JQueryFormValueProvider.cs index e1e319dd95..439dd1a556 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/JQueryFormValueProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/JQueryFormValueProvider.cs @@ -73,16 +73,10 @@ public override async Task GetValueAsync(string key) string[] values; if (dictionary.TryGetValue(key, out values) && values != null && values.Length > 0) { - // Success. - if (values.Length == 1) - { - return new ValueProviderResult(values[0], values[0], Culture); - } - - return new ValueProviderResult(values, string.Join(",", values), Culture); + return new ValueProviderResult(values, Culture); } - return null; + return ValueProviderResult.None; } private async Task> GetDictionary() diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ReadableStringCollectionValueProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ReadableStringCollectionValueProvider.cs index 346617eea9..c47be559f4 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ReadableStringCollectionValueProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ReadableStringCollectionValueProvider.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.Framework.Internal; @@ -85,16 +86,11 @@ public override async Task GetValueAsync([NotNull] string k ValueProviderResult result; if (values == null) { - result = null; - } - else if (values.Count == 1) - { - var value = (string)values[0]; - result = new ValueProviderResult(value, value, _culture); + result = ValueProviderResult.None; } else { - result = new ValueProviderResult(values, _values.Get(key), _culture); + result = new ValueProviderResult(values.ToArray(), _culture); } return result; diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/TypeConverterModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs similarity index 73% rename from src/Microsoft.AspNet.Mvc.Core/ModelBinding/TypeConverterModelBinder.cs rename to src/Microsoft.AspNet.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs index bc3859e0e2..efab3c0971 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/TypeConverterModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs @@ -8,12 +8,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { - public sealed class TypeConverterModelBinder : IModelBinder + public class SimpleTypeModelBinder : IModelBinder { public async Task BindModelAsync(ModelBindingContext bindingContext) { - ModelBindingHelper.ValidateBindingContext(bindingContext); - if (bindingContext.ModelMetadata.IsComplexType) { // this type cannot be converted @@ -21,38 +19,48 @@ public async Task BindModelAsync(ModelBindingContext binding } var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); - if (valueProviderResult == null) + if (valueProviderResult == ValueProviderResult.None) { // no entry return null; } - object newModel; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); + try { - newModel = valueProviderResult.ConvertTo(bindingContext.ModelType); - ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref newModel); + var model = valueProviderResult.ConvertTo(bindingContext.ModelType); + + if (bindingContext.ModelType == typeof(string)) + { + var modelAsString = model as string; + if (bindingContext.ModelMetadata.ConvertEmptyStringToNull && + string.IsNullOrWhiteSpace(modelAsString)) + { + model = null; + } + } + var isModelSet = true; // When converting newModel a null value may indicate a failed conversion for an otherwise required // model (can't set a ValueType to null). This detects if a null model value is acceptable given the // current bindingContext. If not, an error is logged. - if (newModel == null && !AllowsNullValue(bindingContext.ModelType)) + if (model == null && !AllowsNullValue(bindingContext.ModelType)) { bindingContext.ModelState.TryAddModelError( bindingContext.ModelName, - Resources.FormatCommon_ValueNotValidForProperty(newModel)); + Resources.FormatCommon_ValueNotValidForProperty(model)); isModelSet = false; } // Include a ModelValidationNode if binding succeeded. var validationNode = isModelSet ? - new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, newModel) : + new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model) : null; - return new ModelBindingResult(newModel, bindingContext.ModelName, isModelSet, validationNode); + return new ModelBindingResult(model, bindingContext.ModelName, isModelSet, validationNode); } catch (Exception ex) { diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/TypeMatchModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/TypeMatchModelBinder.cs deleted file mode 100644 index b7b6f45ced..0000000000 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/TypeMatchModelBinder.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) .NET Foundation. 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.Reflection; -using System.Threading.Tasks; -using Microsoft.Framework.Internal; - -namespace Microsoft.AspNet.Mvc.ModelBinding -{ - public sealed class TypeMatchModelBinder : IModelBinder - { - public async Task BindModelAsync(ModelBindingContext bindingContext) - { - var valueProviderResult = await GetCompatibleValueProviderResult(bindingContext); - if (valueProviderResult == null) - { - // conversion would have failed - return null; - } - - bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); - var model = valueProviderResult.RawValue; - ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref model); - var validationNode = new ModelValidationNode( - bindingContext.ModelName, - bindingContext.ModelMetadata, - model); - - return new ModelBindingResult( - model, - bindingContext.ModelName, - isModelSet: true, - validationNode: validationNode); - } - - internal static async Task GetCompatibleValueProviderResult(ModelBindingContext context) - { - ModelBindingHelper.ValidateBindingContext(context); - - var valueProviderResult = await context.ValueProvider.GetValueAsync(context.ModelName); - if (valueProviderResult == null) - { - return null; // the value doesn't exist - } - - if (!IsCompatibleWith(context.ModelType, valueProviderResult.RawValue)) - { - return null; // value is of incompatible type - } - - return valueProviderResult; - } - - private static bool IsCompatibleWith([NotNull] Type type, object value) - { - if (value == null) - { - return !type.GetTypeInfo().IsValueType || Nullable.GetUnderlyingType(type) != null; - } - else - { - return type.GetTypeInfo().IsAssignableFrom(value.GetType().GetTypeInfo()); - } - } - } -} diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ValueProviderResultExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ValueProviderResultExtensions.cs new file mode 100644 index 0000000000..0cee5ab03b --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ValueProviderResultExtensions.cs @@ -0,0 +1,42 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + /// + /// Extensions methods for . + /// + public static class ValueProviderResultExtensions + { + /// + /// Attempts to convert the values in to the specified type. + /// + /// The for conversion. + /// The . + /// + /// The converted value, or the default value of if the value could not be converted. + /// + public static T ConvertTo(this ValueProviderResult result) + { + var valueToConvert = (object)result.Values ?? (object)result.Value; + return ModelBindingHelper.ConvertTo(valueToConvert, result.Culture); + } + + /// + /// Attempts to convert the values in to the specified type. + /// + /// The . + /// The for conversion. + /// + /// The converted value, or the default value of if the value could not be converted. + /// + public static object ConvertTo(this ValueProviderResult result, [NotNull] Type type) + { + var valueToConvert = (object)result.Values ?? (object)result.Value; + return ModelBindingHelper.ConvertTo(valueToConvert, type, result.Culture); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBindingHelper.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBindingHelper.cs index 59fb7a58f2..fb857b24d0 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBindingHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBindingHelper.cs @@ -2,10 +2,14 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.ExceptionServices; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Mvc.Core; @@ -488,16 +492,6 @@ internal static TModel CastOrDefault(object model) return (model is TModel) ? (TModel)model : default(TModel); } - internal static void ReplaceEmptyStringWithNull(ModelMetadata modelMetadata, ref object model) - { - if (model is string && - modelMetadata.ConvertEmptyStringToNull && - string.IsNullOrWhiteSpace(model as string)) - { - model = null; - } - } - public static object ConvertValuesToCollectionType(Type modelType, IList values) { // There's a limited set of collection types we can support here. @@ -539,5 +533,196 @@ public static object ConvertValuesToCollectionType(Type modelType, IList v return null; } } + + /// + /// Converts the provided to a value of + /// using the . + /// + /// The for conversion. + /// The value to convert."/> + /// + /// The converted value or the default value of if the value could not be converted. + /// + public static T ConvertTo(object value) + { + return ConvertTo(value, culture: null); + } + + /// + /// Converts the provided to a value of . + /// + /// The for conversion. + /// The value to convert."/> + /// The for conversion. + /// + /// The converted value or the default value of if the value could not be converted. + /// + public static T ConvertTo(object value, CultureInfo culture) + { + var converted = ConvertTo(value, typeof(T), culture); + return converted == null ? default(T) : (T)converted; + } + + /// + /// Converts the provided to a value of + /// using the . + /// + /// The value to convert."/> + /// The for conversion. + /// + /// The converted value or null if the value could not be converted. + /// + public static object ConvertTo(object value, [NotNull] Type type) + { + return ConvertTo(value, type, culture: null); + } + + /// + /// Converts the provided to a value of . + /// + /// The value to convert."/> + /// The for conversion. + /// The for conversion. + /// + /// The converted value or null if the value could not be converted. + /// + public static object ConvertTo(object value, [NotNull] Type type, CultureInfo culture) + { + if (value == null) + { + // For value types, treat null values as though they were the default value for the type. + return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : null; + } + + if (value.GetType().IsAssignableFrom(type)) + { + return value; + } + + var cultureToUse = culture ?? CultureInfo.InvariantCulture; + return UnwrapPossibleArrayType(value, type, cultureToUse); + } + + private static object UnwrapPossibleArrayType(object value, Type destinationType, CultureInfo culture) + { + // array conversion results in four cases, as below + var valueAsArray = value as Array; + if (destinationType.IsArray) + { + var destinationElementType = destinationType.GetElementType(); + if (valueAsArray != null) + { + // case 1: both destination + source type are arrays, so convert each element + var converted = (IList)Array.CreateInstance(destinationElementType, valueAsArray.Length); + for (var i = 0; i < valueAsArray.Length; i++) + { + converted[i] = ConvertSimpleType(valueAsArray.GetValue(i), destinationElementType, culture); + } + return converted; + } + else + { + // case 2: destination type is array but source is single element, so wrap element in + // array + convert + var element = ConvertSimpleType(value, destinationElementType, culture); + var converted = (IList)Array.CreateInstance(destinationElementType, 1); + converted[0] = element; + return converted; + } + } + else if (valueAsArray != null) + { + // case 3: destination type is single element but source is array, so extract first element + convert + if (valueAsArray.Length > 0) + { + value = valueAsArray.GetValue(0); + return ConvertSimpleType(value, destinationType, culture); + } + else + { + // case 3(a): source is empty array, so can't perform conversion + return null; + } + } + + // case 4: both destination + source type are single elements, so convert + return ConvertSimpleType(value, destinationType, culture); + } + + private static object ConvertSimpleType(object value, Type destinationType, CultureInfo culture) + { + if (value == null || value.GetType().IsAssignableFrom(destinationType)) + { + return value; + } + + // In case of a Nullable object, we try again with its underlying type. + destinationType = UnwrapNullableType(destinationType); + + // if this is a user-input value but the user didn't type anything, return no value + var valueAsString = value as string; + if (valueAsString != null && string.IsNullOrWhiteSpace(valueAsString)) + { + return null; + } + + var converter = TypeDescriptor.GetConverter(destinationType); + var canConvertFrom = converter.CanConvertFrom(value.GetType()); + if (!canConvertFrom) + { + converter = TypeDescriptor.GetConverter(value.GetType()); + } + if (!(canConvertFrom || converter.CanConvertTo(destinationType))) + { + // EnumConverter cannot convert integer, so we verify manually + if (destinationType.GetTypeInfo().IsEnum && + (value is int || + value is uint || + value is long || + value is ulong || + value is short || + value is ushort || + value is byte || + value is sbyte)) + { + return Enum.ToObject(destinationType, value); + } + + throw new InvalidOperationException( + Resources.FormatValueProviderResult_NoConverterExists(value.GetType(), destinationType)); + } + + try + { + return canConvertFrom + ? converter.ConvertFrom(null, culture, value) + : converter.ConvertTo(null, culture, value, destinationType); + } + catch (FormatException) + { + throw; + } + catch (Exception ex) + { + if (ex.InnerException == null) + { + throw; + } + else + { + // TypeConverter throws System.Exception wrapping the FormatException, + // so we throw the inner exception. + ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); + + // This code is never reached because the previous line will always throw. + throw; + } + } + } + + private static Type UnwrapNullableType(Type destinationType) + { + return Nullable.GetUnderlyingType(destinationType) ?? destinationType; + } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/MvcCoreMvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc.Core/MvcCoreMvcOptionsSetup.cs index d0177b9cf5..aee892991d 100644 --- a/src/Microsoft.AspNet.Mvc.Core/MvcCoreMvcOptionsSetup.cs +++ b/src/Microsoft.AspNet.Mvc.Core/MvcCoreMvcOptionsSetup.cs @@ -28,8 +28,7 @@ public static void ConfigureMvc(MvcOptions options) options.ModelBinders.Add(new ServicesModelBinder()); options.ModelBinders.Add(new BodyModelBinder()); options.ModelBinders.Add(new HeaderModelBinder()); - options.ModelBinders.Add(new TypeConverterModelBinder()); - options.ModelBinders.Add(new TypeMatchModelBinder()); + options.ModelBinders.Add(new SimpleTypeModelBinder()); options.ModelBinders.Add(new CancellationTokenModelBinder()); options.ModelBinders.Add(new ByteArrayModelBinder()); options.ModelBinders.Add(new FormFileModelBinder()); diff --git a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs index 2431de95bf..99077dab81 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs @@ -1162,6 +1162,38 @@ internal static string FormatValidatableObjectAdapter_IncompatibleType(object p0 return string.Format(CultureInfo.CurrentCulture, GetString("ValidatableObjectAdapter_IncompatibleType"), p0, p1); } + /// + /// Cannot convert value '{0}' to enum type '{1}'. + /// + internal static string ValueProviderResult_CannotConvertEnum + { + get { return GetString("ValueProviderResult_CannotConvertEnum"); } + } + + /// + /// Cannot convert value '{0}' to enum type '{1}'. + /// + internal static string FormatValueProviderResult_CannotConvertEnum(object p0, object p1) + { + return string.Format(CultureInfo.CurrentCulture, GetString("ValueProviderResult_CannotConvertEnum"), p0, p1); + } + + /// + /// The parameter conversion from type '{0}' to type '{1}' failed because no type converter can convert between these types. + /// + internal static string ValueProviderResult_NoConverterExists + { + get { return GetString("ValueProviderResult_NoConverterExists"); } + } + + /// + /// The parameter conversion from type '{0}' to type '{1}' failed because no type converter can convert between these types. + /// + internal static string FormatValueProviderResult_NoConverterExists(object p0, object p1) + { + return string.Format(CultureInfo.CurrentCulture, GetString("ValueProviderResult_NoConverterExists"), p0, p1); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNet.Mvc.Core/Resources.resx b/src/Microsoft.AspNet.Mvc.Core/Resources.resx index 679efad938..58b8251c8c 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Resources.resx +++ b/src/Microsoft.AspNet.Mvc.Core/Resources.resx @@ -1,17 +1,17 @@  - @@ -342,4 +342,10 @@ The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'. + + Cannot convert value '{0}' to enum type '{1}'. + + + The parameter conversion from type '{0}' to type '{1}' failed because no type converter can convert between these types. + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/Html/DefaultHtmlGenerator.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/Html/DefaultHtmlGenerator.cs index 32099a4242..be877b4951 100644 --- a/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/Html/DefaultHtmlGenerator.cs +++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/Html/DefaultHtmlGenerator.cs @@ -506,9 +506,9 @@ public virtual TagBuilder GenerateTextArea( viewContext.ViewData.ModelState.TryGetValue(fullName, out modelState); var value = string.Empty; - if (modelState != null && modelState.Value != null) + if (modelState != null && modelState.AttemptedValue != null) { - value = modelState.Value.AttemptedValue; + value = modelState.AttemptedValue; } else if (modelExplorer.Model != null) { @@ -940,9 +940,9 @@ internal static string GetFullHtmlFieldName(ViewContext viewContext, string expr internal static object GetModelStateValue(ViewContext viewContext, string key, Type destinationType) { ModelState modelState; - if (viewContext.ViewData.ModelState.TryGetValue(key, out modelState) && modelState.Value != null) + if (viewContext.ViewData.ModelState.TryGetValue(key, out modelState) && modelState.RawValue != null) { - return modelState.Value.ConvertTo(destinationType, culture: null); + return ModelBindingHelper.ConvertTo(modelState.RawValue, destinationType, culture: null); } return null; diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/Html/ValidationHelpers.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/Html/ValidationHelpers.cs index 5e2cd58f85..ae76aef9b5 100644 --- a/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/Html/ValidationHelpers.cs +++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/Rendering/Html/ValidationHelpers.cs @@ -24,8 +24,7 @@ public static string GetUserErrorMessageOrDefault(ModelError modelError, ModelSt return string.Empty; } - var attemptedValue = (modelState.Value != null) ? modelState.Value.AttemptedValue : "null"; - + var attemptedValue = modelState.AttemptedValue ?? "null"; return Resources.FormatCommon_ValueNotValidForProperty(attemptedValue); } diff --git a/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelStateDictionaryTest.cs b/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelStateDictionaryTest.cs index 77bcf18dde..633ecb9a6a 100644 --- a/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelStateDictionaryTest.cs +++ b/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelStateDictionaryTest.cs @@ -18,7 +18,6 @@ public void MarkFieldSkipped_MarksFieldAsSkipped_IfStateIsNotInValid(ModelValida // Arrange var modelState = new ModelState { - Value = GetValueProviderResult("value"), ValidationState = validationState }; @@ -40,7 +39,6 @@ public void MarkFieldSkipped_MarksFieldAsSkipped_IfKeyIsNotPresent() // Arrange var modelState = new ModelState { - Value = GetValueProviderResult("value"), ValidationState = ModelValidationState.Valid }; @@ -63,7 +61,6 @@ public void MarkFieldSkipped_Throws_IfStateIsInvalid() // Arrange var modelState = new ModelState { - Value = GetValueProviderResult("value"), ValidationState = ModelValidationState.Invalid }; @@ -89,7 +86,6 @@ public void MarkFieldValid_MarksFieldAsValid_IfStateIsNotInvalid(ModelValidation // Arrange var modelState = new ModelState { - Value = GetValueProviderResult("value"), ValidationState = validationState }; @@ -126,7 +122,6 @@ public void MarkFieldValid_Throws_IfStateIsInvalid() // Arrange var modelState = new ModelState { - Value = GetValueProviderResult("value"), ValidationState = ModelValidationState.Invalid }; @@ -148,10 +143,7 @@ public void MarkFieldValid_Throws_IfStateIsInvalid() public void CopyConstructor_CopiesModelStateData() { // Arrange - var modelState = new ModelState - { - Value = GetValueProviderResult("value") - }; + var modelState = new ModelState(); var source = new ModelStateDictionary { { "key", modelState } @@ -211,7 +203,7 @@ public void ConstructorWithDictionaryParameter() // Arrange var oldDictionary = new ModelStateDictionary() { - { "foo", new ModelState() { Value = GetValueProviderResult("bar", "bar") } } + { "foo", new ModelState() { RawValue = "bar" } } }; // Act @@ -219,7 +211,7 @@ public void ConstructorWithDictionaryParameter() // Assert Assert.Single(newDictionary); - Assert.Equal("bar", newDictionary["foo"].Value.ConvertTo(typeof(string))); + Assert.Equal("bar", newDictionary["foo"].RawValue); } [Fact] @@ -238,7 +230,7 @@ public void GetFieldValidationState_ReturnsUnvalidatedIfDictionaryDoesNotContain [Fact] public void GetValidationState_ReturnsValidationStateForKey_IgnoresChildren() { - // Arrange + // Arrange var msd = new ModelStateDictionary(); msd.AddModelError("foo.bar", "error text"); @@ -277,7 +269,6 @@ public void GetFieldValidationState_ReturnsValidIfModelStateDoesNotContainErrors // Arrange var validState = new ModelState { - Value = new ValueProviderResult(rawValue: null), ValidationState = ModelValidationState.Valid }; var msd = new ModelStateDictionary @@ -316,7 +307,6 @@ public void GetFieldValidationState_IndexedPrefix_ReturnsValidIfModelStateDoesNo // Arrange var validState = new ModelState { - Value = new ValueProviderResult(rawValue: null), ValidationState = ModelValidationState.Valid }; var msd = new ModelStateDictionary @@ -337,12 +327,10 @@ public void IsValidPropertyReturnsFalseIfErrors() // Arrange var errorState = new ModelState { - Value = GetValueProviderResult("quux", "quux"), ValidationState = ModelValidationState.Invalid }; var validState = new ModelState { - Value = GetValueProviderResult("bar", "bar"), ValidationState = ModelValidationState.Valid }; errorState.Errors.Add("some error"); @@ -370,13 +358,11 @@ public void IsValidPropertyReturnsTrueIfNoErrors() { "foo", new ModelState { ValidationState = ModelValidationState.Valid, - Value = GetValueProviderResult("bar", "bar") } }, { "baz", new ModelState { ValidationState = ModelValidationState.Skipped, - Value = GetValueProviderResult("quux", "bar") } } }; @@ -396,12 +382,10 @@ public void IsValidPropertyReturnsFalse_IfSomeFieldsAreNotValidated() // Arrange var errorState = new ModelState { - Value = GetValueProviderResult("quux", "quux"), ValidationState = ModelValidationState.Invalid }; var validState = new ModelState { - Value = GetValueProviderResult("bar", "bar"), ValidationState = ModelValidationState.Valid }; errorState.Errors.Add("some error"); @@ -409,7 +393,7 @@ public void IsValidPropertyReturnsFalse_IfSomeFieldsAreNotValidated() { { "foo", validState }, { "baz", errorState }, - { "qux", new ModelState { Value = GetValueProviderResult() }} + { "qux", new ModelState() } }; // Act @@ -457,14 +441,15 @@ public void SetAttemptedValueCreatesModelStateIfNotPresent() var dictionary = new ModelStateDictionary(); // Act - dictionary.SetModelValue("some key", GetValueProviderResult("some value", "some value")); + dictionary.SetModelValue("some key", new string[] { "some value" }, "some value"); // Assert Assert.Single(dictionary); var modelState = dictionary["some key"]; Assert.Empty(modelState.Errors); - Assert.Equal("some value", modelState.Value.ConvertTo(typeof(string))); + Assert.Equal(new string[] { "some value" }, modelState.RawValue); + Assert.Equal("some value", modelState.AttemptedValue); } [Fact] @@ -476,7 +461,7 @@ public void SetAttemptedValueUsesExistingModelStateIfPresent() var ex = new Exception(); // Act - dictionary.SetModelValue("some key", GetValueProviderResult("some value", "some value")); + dictionary.SetModelValue("some key", new string[] { "some value" }, "some value"); // Assert Assert.Single(dictionary); @@ -484,7 +469,8 @@ public void SetAttemptedValueUsesExistingModelStateIfPresent() Assert.Single(modelState.Errors); Assert.Equal("some error", modelState.Errors[0].ErrorMessage); - Assert.Equal("some value", modelState.Value.ConvertTo(typeof(string))); + Assert.Equal(new string[] { "some value" }, modelState.RawValue); + Assert.Equal("some value", modelState.AttemptedValue); } [Fact] @@ -492,7 +478,7 @@ public void GetFieldValidity_ReturnsUnvalidated_IfNoEntryExistsForKey() { // Arrange var dictionary = new ModelStateDictionary(); - dictionary.SetModelValue("user.Name", GetValueProviderResult()); + dictionary.SetModelValue("user.Name", new string[] { "some value" }, "some value"); // Act var validationState = dictionary.GetFieldValidationState("not-user"); @@ -507,7 +493,7 @@ public void GetFieldValidity_ReturnsUnvalidated_IfAnyItemInSubtreeIsInvalid() // Arrange var dictionary = new ModelStateDictionary(); dictionary["user.Address"] = new ModelState { ValidationState = ModelValidationState.Valid }; - dictionary.SetModelValue("user.Name", GetValueProviderResult()); + dictionary.SetModelValue("user.Name", new string[] { "some value" }, "some value"); dictionary.AddModelError("user.Age", "Age is not a valid int"); // Act @@ -745,7 +731,7 @@ public void ModelStateDictionary_ReturnSpecificErrorMessage_WhenModelStateSet() // Arrange var expected = "The value 'some value' is not valid for key."; var dictionary = new ModelStateDictionary(); - dictionary.SetModelValue("key", GetValueProviderResult()); + dictionary.SetModelValue("key", new string[] { "some value" }, "some value"); // Act dictionary.TryAddModelError("key", new FormatException()); @@ -760,7 +746,7 @@ public void ModelStateDictionary_NoErrorMessage_ForNonFormatException() { // Arrange var dictionary = new ModelStateDictionary(); - dictionary.SetModelValue("key", GetValueProviderResult()); + dictionary.SetModelValue("key", new string[] { "some value" }, "some value"); // Act dictionary.TryAddModelError("key", new InvalidOperationException()); @@ -905,12 +891,5 @@ public void ModelStateDictionary_ClearsAllEntries_EmptyKey(string modelKey) Assert.Equal(0, dictionary["Property4"].Errors.Count); Assert.Equal(ModelValidationState.Unvalidated, dictionary["Property4"].ValidationState); } - - private static ValueProviderResult GetValueProviderResult(object rawValue = null, string attemptedValue = null) - { - return new ValueProviderResult(rawValue ?? "some value", - attemptedValue ?? "some value", - CultureInfo.InvariantCulture); - } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ValueProviderResultTest.cs b/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ValueProviderResultTest.cs index 6214865b27..ea875a1ad1 100644 --- a/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ValueProviderResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ValueProviderResultTest.cs @@ -1,9 +1,6 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. 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.Globalization; using Xunit; namespace Microsoft.AspNet.Mvc.ModelBinding @@ -11,563 +8,190 @@ namespace Microsoft.AspNet.Mvc.ModelBinding public class ValueProviderResultTest { [Fact] - public void ConvertTo_ReturnsNullForReferenceTypes_WhenValueIsNull() + public void Construct_With_NullString() { - var valueProviderResult = new ValueProviderResult(rawValue: null); - - var convertedValue = valueProviderResult.ConvertTo(typeof(string)); - - Assert.Null(convertedValue); - } - - [Fact] - public void ConvertTo_ReturnsDefaultForValueTypes_WhenValueIsNull() - { - var valueProviderResult = new ValueProviderResult(rawValue: null); - - var convertedValue = valueProviderResult.ConvertTo(typeof(int)); - - Assert.Equal(0, convertedValue); - } - - [Fact] - public void ConvertToCanConvertArraysToSingleElements() - { - // Arrange - var valueProviderResult = new ValueProviderResult( - new int[] { 1, 20, 42 }, - string.Empty, - CultureInfo.InvariantCulture); - - // Act - var converted = (string)valueProviderResult.ConvertTo(typeof(string)); - - // Assert - Assert.Equal("1", converted); - } - - [Fact] - public void ConvertToCanConvertSingleElementsToArrays() - { - // Arrange - var valueProviderResult = new ValueProviderResult(42, string.Empty, CultureInfo.InvariantCulture); - - // Act - var converted = (string[])valueProviderResult.ConvertTo(typeof(string[])); - - // Assert - Assert.NotNull(converted); - var result = Assert.Single(converted); - Assert.Equal("42", result); - } - - [Fact] - public void ConvertToCanConvertSingleElementsToSingleElements() - { - // Arrange - var valueProviderResult = new ValueProviderResult(42, string.Empty, CultureInfo.InvariantCulture); - - // Act - var converted = (string)valueProviderResult.ConvertTo(typeof(string)); - - // Assert - Assert.NotNull(converted); - Assert.Equal("42", converted); - } - - [Fact] - public void ConvertingNullStringToNullableIntReturnsNull() - { - // Arrange - object original = null; - var valueProviderResult = new ValueProviderResult(original, string.Empty, CultureInfo.InvariantCulture); - - // Act - var returned = (int?)valueProviderResult.ConvertTo(typeof(int?)); - - // Assert - Assert.Equal(returned, null); - } - - [Fact] - public void ConvertingWhiteSpaceStringToNullableIntReturnsNull() - { - // Arrange - var original = " "; - var valueProviderResult = new ValueProviderResult(original, string.Empty, CultureInfo.InvariantCulture); - - // Act - var returned = (int?)valueProviderResult.ConvertTo(typeof(int?)); - - // Assert - Assert.Equal(returned, null); - } - - [Fact] - public void ConvertToReturnsNullIfArrayElementValueIsNull() - { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: new string[] { null }); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(int)); - - // Assert - Assert.Null(outValue); - } - - [Fact] - public void ConvertToReturnsNullIfTryingToConvertEmptyArrayToSingleElement() - { - // Arrange - var valueProviderResult = new ValueProviderResult(new int[0], string.Empty, CultureInfo.InvariantCulture); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(int)); - - // Assert - Assert.Null(outValue); - } - - [Theory] - [InlineData("")] - [InlineData(" \t \r\n ")] - public void ConvertToReturnsNullIfTrimmedValueIsEmptyString(object value) - { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: value); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(int)); - - // Assert - Assert.Null(outValue); - } - - [Fact] - public void ConvertToReturnsNullIfTrimmedValueIsEmptyString() - { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: null); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(int[])); - - // Assert - Assert.Null(outValue); - } - - [Fact] - public void ConvertToReturnsValueIfArrayElementIsIntegerAndDestinationTypeIsEnum() - { - // Arrange - var result = new ValueProviderResult(rawValue: new object[] { 1 }); - - // Act - var outValue = result.ConvertTo(typeof(IntEnum)); - - // Assert - Assert.Equal(outValue, IntEnum.Value1); - } - - [Theory] - [InlineData(1, typeof(IntEnum), IntEnum.Value1)] - [InlineData(1L, typeof(LongEnum), LongEnum.Value1)] - [InlineData(long.MaxValue, typeof(LongEnum), LongEnum.MaxValue)] - [InlineData(1U, typeof(UnsignedIntEnum), UnsignedIntEnum.Value1)] - [InlineData(1UL, typeof(IntEnum), IntEnum.Value1)] - [InlineData((byte)1, typeof(ByteEnum), ByteEnum.Value1)] - [InlineData(byte.MaxValue, typeof(ByteEnum), ByteEnum.MaxValue)] - [InlineData((sbyte)1, typeof(ByteEnum), ByteEnum.Value1)] - [InlineData((short)1, typeof(IntEnum), IntEnum.Value1)] - [InlineData((ushort)1, typeof(IntEnum), IntEnum.Value1)] - [InlineData(int.MaxValue, typeof(IntEnum?), IntEnum.MaxValue)] - [InlineData(null, typeof(IntEnum?), null)] - [InlineData(1L, typeof(LongEnum?), LongEnum.Value1)] - [InlineData(null, typeof(LongEnum?), null)] - [InlineData(uint.MaxValue, typeof(UnsignedIntEnum?), UnsignedIntEnum.MaxValue)] - [InlineData((byte)1, typeof(ByteEnum?), ByteEnum.Value1)] - [InlineData(null, typeof(ByteEnum?), null)] - [InlineData((ushort)1, typeof(LongEnum?), LongEnum.Value1)] - public void ConvertToReturnsValueIfArrayElementIsAnyIntegerTypeAndDestinationTypeIsEnum( - object input, - Type enumType, - object expected) - { - // Arrange - var result = new ValueProviderResult(rawValue: new object[] { input }); - - // Act - var outValue = result.ConvertTo(enumType); - - // Assert - Assert.Equal(expected, outValue); - } - - [Fact] - public void ConvertToReturnsValueIfArrayElementIsStringValueAndDestinationTypeIsEnum() - { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: new object[] { "1" }); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(IntEnum)); - - // Assert - Assert.Equal(outValue, IntEnum.Value1); - } - - [Fact] - public void ConvertToReturnsValueIfArrayElementIsStringKeyAndDestinationTypeIsEnum() - { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: new object[] { "Value1" }); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(IntEnum)); + // Arrange & Act + var result = new ValueProviderResult((string)null); // Assert - Assert.Equal(outValue, IntEnum.Value1); + Assert.Equal(0, result.Length); + Assert.Null(result.Value); + Assert.Empty(result.Values); + Assert.Null(result.FirstValue); + Assert.Equal(ValueProviderResult.None, result); + Assert.Null((string)result); + Assert.Empty((string[])result); } [Fact] - public void ConvertToReturnsValueIfElementIsStringAndDestinationIsNullableInteger() + public void Construct_With_NullArray() { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: "12"); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(int?)); - - // Assert - Assert.Equal(12, outValue); - } - - [Fact] - public void ConvertToReturnsValueIfElementIsStringAndDestinationIsNullableDouble() - { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: "12.5"); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(double?)); - - // Assert - Assert.Equal(12.5, outValue); - } - - [Fact] - public void ConvertToReturnsValueIfElementIsDecimalAndDestinationIsNullableInteger() - { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: 12M); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(int?)); + // Arrange & Act + var result = new ValueProviderResult((string[])null); // Assert - Assert.Equal(12, outValue); + Assert.Equal(0, result.Length); + Assert.Null(result.Value); + Assert.Empty(result.Values); + Assert.Null(result.FirstValue); + Assert.Equal(ValueProviderResult.None, result); + Assert.Null((string)result); + Assert.Empty((string[])result); } [Fact] - public void ConvertToReturnsValueIfElementIsDecimalAndDestinationIsNullableDouble() + public void Construct_With_None() { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: 12.5M); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(double?)); + // Arrange & Act + var result = ValueProviderResult.None; // Assert - Assert.Equal(12.5, outValue); + Assert.Equal(0, result.Length); + Assert.Null(result.Value); + Assert.Empty(result.Values); + Assert.Null(result.FirstValue); + Assert.Equal(ValueProviderResult.None, result); + Assert.Null((string)result); + Assert.Empty((string[])result); } [Fact] - public void ConvertToReturnsValueIfElementIsDecimalDoubleAndDestinationIsNullableInteger() + public void Construct_With_String() { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: 12.5M); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(int?)); + // Arrange & Act + var result = new ValueProviderResult("Hi There"); // Assert - Assert.Equal(12, outValue); + Assert.Equal(1, result.Length); + Assert.Equal("Hi There", result.Value); + Assert.Null(result.Values); + Assert.Equal("Hi There", result.FirstValue); + Assert.NotEqual(ValueProviderResult.None, result); + Assert.Equal("Hi There", (string)result); + Assert.Equal(new string[] { "Hi There" }, (string[])result); } [Fact] - public void ConvertToReturnsValueIfElementIsDecimalDoubleAndDestinationIsNullableLong() + public void Construct_With_Array() { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: 12.5M); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(long?)); + // Arrange & Act + var result = new ValueProviderResult(new string[] { "Hi", "There" }); // Assert - Assert.Equal(12L, outValue); - } - - [Fact] - public void ConvertToReturnsValueIfArrayElementInstanceOfDestinationType() - { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: new object[] { "some string" }); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(string)); - - // Assert - Assert.Equal("some string", outValue); - } - - [Theory] - [InlineData(new object[] { new[] { 1, 0 } })] - [InlineData(new object[] { new[] { "Value1", "Value0" } })] - [InlineData(new object[] { new[] { "Value1", "value0" } })] - public void ConvertTo_ConvertsEnumArrays(object value) - { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: value); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(IntEnum[])); - - // Assert - var result = Assert.IsType(outValue); Assert.Equal(2, result.Length); - Assert.Equal(IntEnum.Value1, result[0]); - Assert.Equal(IntEnum.Value0, result[1]); - } - - [Theory] - [InlineData(new object[] { new[] { 1, 2 }, new[] { FlagsEnum.Value1, FlagsEnum.Value2 } })] - [InlineData(new object[] { new[] { "Value1", "Value2" }, new[] { FlagsEnum.Value1, FlagsEnum.Value2 } })] - [InlineData(new object[] { new[] { 5, 2 }, new[] { FlagsEnum.Value1 | FlagsEnum.Value4, FlagsEnum.Value2 } })] - public void ConvertTo_ConvertsFlagsEnumArrays(object value, FlagsEnum[] expected) - { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: value); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(FlagsEnum[])); - - // Assert - var result = Assert.IsType(outValue); - Assert.Equal(2, result.Length); - Assert.Equal(expected[0], result[0]); - Assert.Equal(expected[1], result[1]); - } - - [Fact] - public void ConvertToReturnsValueIfInstanceOfDestinationType() - { - // Arrange - var original = new[] { "some string" }; - var valueProviderResult = new ValueProviderResult(rawValue: original); - - // Act - var outValue = valueProviderResult.ConvertTo(typeof(string[])); - - // Assert - Assert.Same(original, outValue); - } - - [Theory] - [InlineData(typeof(int))] - [InlineData(typeof(double?))] - [InlineData(typeof(IntEnum?))] - public void ConvertToThrowsIfConverterThrows(Type destinationType) - { - // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: "this-is-not-a-valid-value"); - - // Act & Assert - var ex = Assert.Throws(typeof(FormatException), () => valueProviderResult.ConvertTo(destinationType)); + Assert.Null(result.Value); + Assert.Equal(new string[] { "Hi", "There" }, result.Values); + Assert.Equal("Hi", result.FirstValue); + Assert.NotEqual(ValueProviderResult.None, result); + Assert.Equal("Hi,There", (string)result); + Assert.Equal(new string[] { "Hi", "There" }, (string[])result); } [Fact] - public void ConvertToThrowsIfNoConverterExists() + public void Enumerator_WithString() { // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: "x"); - var destinationType = typeof(MyClassWithoutConverter); + var result = new ValueProviderResult("Hi There"); // Act & Assert - var ex = Assert.Throws(() => valueProviderResult.ConvertTo(destinationType)); - Assert.Equal("The parameter conversion from type 'System.String' to type " + - "'Microsoft.AspNet.Mvc.ModelBinding.ValueProviderResultTest+MyClassWithoutConverter' " + - "failed because no type converter can convert between these types.", - ex.Message); - } - - [Fact] - public void ConvertToUsesProvidedCulture() - { - // Arrange - var original = "12,5"; - var valueProviderResult = new ValueProviderResult( - rawValue: original, - attemptedValue: null, - culture: new CultureInfo("en-GB")); - var frCulture = new CultureInfo("fr-FR"); - - // Act - var cultureResult = valueProviderResult.ConvertTo(typeof(decimal), frCulture); - - // Assert - Assert.Equal(12.5M, cultureResult); - Assert.Throws(() => valueProviderResult.ConvertTo(typeof(decimal))); + Assert.Equal(new string[] { "Hi There", }, result); } [Fact] - public void CulturePropertyDefaultsToInvariantCulture() - { - // Arrange - var result = new ValueProviderResult(rawValue: null, attemptedValue: null, culture: null); - - // Act & assert - Assert.Same(CultureInfo.InvariantCulture, result.Culture); - } - - [Theory] - [MemberData(nameof(IntrinsicConversionData))] - public void ConvertToCanConvertIntrinsics(object initialValue, T expectedValue) + public void Enumerator_WithArray() { // Arrange - var result = new ValueProviderResult(initialValue, string.Empty, CultureInfo.InvariantCulture); + var result = new ValueProviderResult(new string[] { "Hi", "There" }); // Act & Assert - Assert.Equal(expectedValue, result.ConvertTo(typeof(T))); + Assert.Equal(new string[] { "Hi", "There" }, result); } - public static IEnumerable IntrinsicConversionData + public static TheoryData EqualsData { get { - yield return new object[] { 42, 42L }; - yield return new object[] { 42, (short)42 }; - yield return new object[] { 42, (float)42.0 }; - yield return new object[] { 42, (double)42.0 }; - yield return new object[] { 42M, 42 }; - yield return new object[] { 42L, 42 }; - yield return new object[] { 42, (byte)42 }; - yield return new object[] { (short)42, 42 }; - yield return new object[] { (float)42.0, 42 }; - yield return new object[] { (double)42.0, 42 }; - yield return new object[] { (byte)42, 42 }; - yield return new object[] { "2008-01-01", new DateTime(2008, 01, 01) }; - yield return new object[] { "00:00:20", TimeSpan.FromSeconds(20) }; - yield return new object[] + return new TheoryData() { - "c6687d3a-51f9-4159-8771-a66d2b7d7038", - Guid.Parse("c6687d3a-51f9-4159-8771-a66d2b7d7038") + { + new ValueProviderResult("Hi"), + new ValueProviderResult("Hi"), + true + }, + { + new ValueProviderResult("Hi"), + new ValueProviderResult(new string[] { "Hi"}), + true + }, + { + new ValueProviderResult(new string[] { "Hi"}), + new ValueProviderResult("Hi"), + true + }, + { + new ValueProviderResult(new string[] { "Hi"}), + new ValueProviderResult(new string[] { "Hi"}), + true + }, + { + new ValueProviderResult(new string[] { "Hi", "There"}), + new ValueProviderResult(new string[] { "Hi", "There"}), + true + }, + { + new ValueProviderResult("Hi,There"), + new ValueProviderResult(new string[] { "Hi", "There"}), + false + }, + { + new ValueProviderResult(new string[] { "Hi", string.Empty }), + new ValueProviderResult(new string[] { "Hi", "There"}), + false + }, + { + new ValueProviderResult(new string[] { "Hi", "There" }), + new ValueProviderResult(new string[] { "Hi", "ThEre"}), + false + }, + { + new ValueProviderResult(new string[] { "Hi", }), + new ValueProviderResult(new string[] { "Hi", string.Empty }), + false + }, + { + new ValueProviderResult(), + new ValueProviderResult((string)null), + true + }, + { + new ValueProviderResult(), + new ValueProviderResult("hi"), + false + }, }; } } [Theory] - [InlineData(typeof(TimeSpan))] - [InlineData(typeof(DateTime))] - [InlineData(typeof(DateTimeOffset))] - [InlineData(typeof(Guid))] - [InlineData(typeof(IntEnum))] - public void ConvertTo_Throws_IfValueIsNotStringData(Type destinationType) + [MemberData(nameof(EqualsData))] + public void Operator_Equals(ValueProviderResult x, ValueProviderResult y, bool expected) { // Arrange - var result = new ValueProviderResult( - new MyClassWithoutConverter(), - string.Empty, - CultureInfo.InvariantCulture); + var result = x == y; - // Act - var ex = Assert.Throws(() => result.ConvertTo(destinationType)); - - // Assert - var expectedMessage = string.Format("The parameter conversion from type '{0}' to type '{1}' " + - "failed because no type converter can convert between these types.", - typeof(MyClassWithoutConverter), destinationType); - Assert.Equal(expectedMessage, ex.Message); - } - - [Fact] - public void ConvertTo_Throws_IfDestinationTypeIsNotConvertible() - { - // Arrange - var value = "Hello world"; - var destinationType = typeof(MyClassWithoutConverter); - var result = new ValueProviderResult(value, string.Empty, CultureInfo.InvariantCulture); - - // Act - var ex = Assert.Throws(() => result.ConvertTo(destinationType)); - - // Assert - var expectedMessage = string.Format("The parameter conversion from type '{0}' to type '{1}' " + - "failed because no type converter can convert between these types.", - value.GetType(), typeof(MyClassWithoutConverter)); - Assert.Equal(expectedMessage, ex.Message); + // Act & Assert + Assert.Equal(expected, result); } [Theory] - [InlineData(new object[] { 2, FlagsEnum.Value2 })] - [InlineData(new object[] { 5, FlagsEnum.Value1 | FlagsEnum.Value4 })] - [InlineData(new object[] { 15, FlagsEnum.Value1 | FlagsEnum.Value2 | FlagsEnum.Value4 | FlagsEnum.Value8 })] - [InlineData(new object[] { 16, (FlagsEnum)16 })] - [InlineData(new object[] { 0, (FlagsEnum)0 })] - [InlineData(new object[] { null, (FlagsEnum)0 })] - [InlineData(new object[] { "Value1,Value2", (FlagsEnum)3 })] - [InlineData(new object[] { "Value1,Value2,value4, value8", (FlagsEnum)15 })] - public void ConvertTo_ConvertsEnumFlags(object value, object expected) + [MemberData(nameof(EqualsData))] + public void Operator_NotEquals(ValueProviderResult x, ValueProviderResult y, bool expected) { // Arrange - var valueProviderResult = new ValueProviderResult(rawValue: value); - - // Act - var outValue = (FlagsEnum)valueProviderResult.ConvertTo(typeof(FlagsEnum)); + var result = x != y; - // Assert - Assert.Equal(expected, outValue); - } - - private class MyClassWithoutConverter - { - } - - private enum IntEnum - { - Value0 = 0, - Value1 = 1, - MaxValue = int.MaxValue - } - - private enum LongEnum : long - { - Value0 = 0L, - Value1 = 1L, - MaxValue = long.MaxValue - } - - private enum UnsignedIntEnum : uint - { - Value0 = 0U, - Value1 = 1U, - MaxValue = uint.MaxValue - } - - private enum ByteEnum : byte - { - Value0 = 0, - Value1 = 1, - MaxValue = byte.MaxValue - } - - [Flags] - public enum FlagsEnum - { - Value1 = 1, - Value2 = 2, - Value4 = 4, - Value8 = 8 + // Act & Assert + Assert.NotEqual(expected, result); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ArrayModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ArrayModelBinderTest.cs index 682c309364..dea769501f 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ArrayModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ArrayModelBinderTest.cs @@ -16,7 +16,7 @@ public class ArrayModelBinderTest public async Task BindModelAsync_ValueProviderContainPrefix_Succeeds() { // Arrange - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "someName[0]", "42" }, { "someName[1]", "84" }, @@ -138,7 +138,7 @@ public static TheoryData ArrayModelData public async Task BindModelAsync_ModelMetadataReadOnly_ReturnsNull(int[] model) { // Arrange - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "someName[0]", "42" }, { "someName[1]", "84" }, @@ -161,7 +161,7 @@ public async Task BindModelAsync_ModelMetadataNotReadOnly_ModelNonNull_FailsSile { // Arrange var arrayLength = model.Length; - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "someName[0]", "42" }, { "someName[1]", "84" }, @@ -196,7 +196,7 @@ private static IModelBinder CreateIntBinder() .Returns(async (ModelBindingContext mbc) => { var value = await mbc.ValueProvider.GetValueAsync(mbc.ModelName); - if (value != null) + if (value != ValueProviderResult.None) { var model = value.ConvertTo(mbc.ModelType); return new ModelBindingResult(model, key: null, isModelSet: true); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ByteArrayModelBinderTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ByteArrayModelBinderTests.cs index 9a88a3673d..4c94bd9cfb 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ByteArrayModelBinderTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ByteArrayModelBinderTests.cs @@ -17,7 +17,7 @@ public class ByteArrayModelBinderTests public async Task BindModelSetsModelToNullOnNullOrEmptyString(string value) { // Arrange - var valueProvider = new SimpleHttpValueProvider() + var valueProvider = new SimpleValueProvider() { { "foo", value } }; @@ -36,15 +36,14 @@ public async Task BindModelSetsModelToNullOnNullOrEmptyString(string value) var modelState = Assert.Single(bindingContext.ModelState); Assert.Equal("foo", modelState.Key); - Assert.NotNull(modelState.Value.Value); - Assert.Equal(value, modelState.Value.Value.RawValue); + Assert.Equal(string.Empty, modelState.Value.RawValue); } [Fact] public async Task BindModel() { // Arrange - var valueProvider = new SimpleHttpValueProvider() + var valueProvider = new SimpleValueProvider() { { "foo", "Fys1" } }; @@ -67,7 +66,7 @@ public async Task BindModelAddsModelErrorsOnInvalidCharacters() // Arrange var expected = "The value '\"Fys1\"' is not valid for foo."; - var valueProvider = new SimpleHttpValueProvider() + var valueProvider = new SimpleValueProvider() { { "foo", "\"Fys1\"" } }; @@ -89,7 +88,7 @@ public async Task BindModelAddsModelErrorsOnInvalidCharacters() public async Task BindModel_ReturnsWithIsModelSetFalse_WhenValueNotFound() { // Arrange - var valueProvider = new SimpleHttpValueProvider() + var valueProvider = new SimpleValueProvider() { { "someName", "" } }; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CancellationTokenModelBinderTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CancellationTokenModelBinderTests.cs index 606804e1ae..31555f0e95 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CancellationTokenModelBinderTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CancellationTokenModelBinderTests.cs @@ -53,7 +53,7 @@ private static ModelBindingContext GetBindingContext(Type modelType) { ModelMetadata = metadataProvider.GetMetadataForType(modelType), ModelName = "someName", - ValueProvider = new SimpleHttpValueProvider(), + ValueProvider = new SimpleValueProvider(), OperationBindingContext = new OperationBindingContext { ModelBinder = new CancellationTokenModelBinder(), diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CollectionModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CollectionModelBinderTest.cs index 6c62049eec..b0b6e1278d 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CollectionModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CollectionModelBinderTest.cs @@ -23,7 +23,7 @@ public class CollectionModelBinderTest public async Task BindComplexCollectionFromIndexes_FiniteIndexes() { // Arrange - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "someName[foo]", "42" }, { "someName[baz]", "200" } @@ -45,7 +45,7 @@ public async Task BindComplexCollectionFromIndexes_FiniteIndexes() public async Task BindComplexCollectionFromIndexes_InfiniteIndexes() { // Arrange - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "someName[0]", "42" }, { "someName[1]", "100" }, @@ -70,7 +70,7 @@ public async Task BindComplexCollectionFromIndexes_InfiniteIndexes() public async Task BindModel_ComplexCollection_Succeeds(bool isReadOnly) { // Arrange - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "someName.index", new[] { "foo", "bar", "baz" } }, { "someName[foo]", "42" }, @@ -100,7 +100,7 @@ public async Task BindModel_ComplexCollection_Succeeds(bool isReadOnly) public async Task BindModel_ComplexCollection_BindingContextModelNonNull_Succeeds(bool isReadOnly) { // Arrange - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "someName.index", new[] { "foo", "bar", "baz" } }, { "someName[foo]", "42" }, @@ -132,7 +132,7 @@ public async Task BindModel_ComplexCollection_BindingContextModelNonNull_Succeed public async Task BindModel_SimpleCollection_Succeeds(bool isReadOnly) { // Arrange - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "someName", new[] { "42", "100", "200" } } }; @@ -149,8 +149,6 @@ public async Task BindModel_SimpleCollection_Succeeds(bool isReadOnly) var list = Assert.IsAssignableFrom>(result.Model); Assert.Equal(new[] { 42, 100, 200 }, list.ToArray()); - - Assert.True(modelState.IsValid); } [Theory] @@ -159,7 +157,7 @@ public async Task BindModel_SimpleCollection_Succeeds(bool isReadOnly) public async Task BindModel_SimpleCollection_BindingContextModelNonNull_Succeeds(bool isReadOnly) { // Arrange - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "someName", new[] { "42", "100", "200" } } }; @@ -178,8 +176,6 @@ public async Task BindModel_SimpleCollection_BindingContextModelNonNull_Succeeds Assert.Same(list, result.Model); Assert.Equal(new[] { 42, 100, 200 }, list.ToArray()); - - Assert.True(modelState.IsValid); } [Fact] @@ -187,7 +183,7 @@ public async Task BindModelAsync_SimpleCollectionWithNullValue_Succeeds() { // Arrange var binder = new CollectionModelBinder(); - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "someName", null }, }; @@ -205,8 +201,6 @@ public async Task BindModelAsync_SimpleCollectionWithNullValue_Succeeds() var model = Assert.IsType>(result.Model); Assert.Empty(model); - - Assert.True(modelState.IsValid); } #endif @@ -225,7 +219,7 @@ public async Task BindSimpleCollection_RawValueIsEmptyCollection_ReturnsEmptyLis }; // Act - var boundCollection = await binder.BindSimpleCollection(context, rawValue: new object[0], culture: null); + var boundCollection = await binder.BindSimpleCollection(context, new ValueProviderResult(new string[0])); // Assert Assert.NotNull(boundCollection.Model); @@ -392,7 +386,7 @@ public async Task BindSimpleCollection_SubBindingSucceeds() { // Arrange var culture = new CultureInfo("fr-FR"); - var bindingContext = GetModelBindingContext(new SimpleHttpValueProvider()); + var bindingContext = GetModelBindingContext(new SimpleValueProvider()); ModelValidationNode childValidationNode = null; Mock.Get(bindingContext.OperationBindingContext.ModelBinder) .Setup(o => o.BindModelAsync(It.IsAny())) @@ -405,7 +399,9 @@ public async Task BindSimpleCollection_SubBindingSucceeds() var modelBinder = new CollectionModelBinder(); // Act - var boundCollection = await modelBinder.BindSimpleCollection(bindingContext, new int[1], culture); + var boundCollection = await modelBinder.BindSimpleCollection( + bindingContext, + new ValueProviderResult(new string[] { "0" })); // Assert Assert.Equal(new[] { 42 }, boundCollection.Model.ToArray()); @@ -442,11 +438,11 @@ private static IModelBinder CreateIntBinder() .Returns(async (ModelBindingContext mbc) => { var value = await mbc.ValueProvider.GetValueAsync(mbc.ModelName); - if (value != null) + if (value != ValueProviderResult.None) { var model = value.ConvertTo(mbc.ModelType); var modelValidationNode = new ModelValidationNode(mbc.ModelName, mbc.ModelMetadata, model); - return new ModelBindingResult(model, mbc.ModelName, true, modelValidationNode); + return new ModelBindingResult(model, mbc.ModelName, model != null, modelValidationNode); } return null; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeModelBinderTest.cs index 5a924f4af5..4ee4418633 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeModelBinderTest.cs @@ -24,7 +24,7 @@ public async Task BindModel_SuccessfulBind_ReturnsModel() ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(int)), ModelName = "someName", ModelState = new ModelStateDictionary(), - ValueProvider = new SimpleHttpValueProvider + ValueProvider = new SimpleValueProvider { { "someName", "dummyValue" } }, @@ -71,7 +71,7 @@ public async Task BindModel_SuccessfulBind_ComplexTypeFallback_ReturnsModel() ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List)), ModelName = "someName", ModelState = new ModelStateDictionary(), - ValueProvider = new SimpleHttpValueProvider + ValueProvider = new SimpleValueProvider { { "someOtherName", "dummyValue" } }, @@ -121,7 +121,7 @@ public async Task ModelBinder_ReturnsNull_IfBinderMatchesButDoesNotSetModel() ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List)), ModelName = "someName", ModelState = new ModelStateDictionary(), - ValueProvider = new SimpleHttpValueProvider + ValueProvider = new SimpleValueProvider { { "someOtherName", "dummyValue" } }, @@ -155,7 +155,7 @@ public async Task ModelBinder_FallsBackToEmpty_IfBinderMatchesButDoesNotSetModel ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List)), ModelName = "someName", ModelState = new ModelStateDictionary(), - ValueProvider = new SimpleHttpValueProvider + ValueProvider = new SimpleValueProvider { { "someOtherName", "dummyValue" } }, @@ -201,7 +201,7 @@ public async Task ModelBinder_DoesNotFallBackToEmpty_IfFallbackToEmptyPrefixFals ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List)), ModelName = "someName", ModelState = new ModelStateDictionary(), - ValueProvider = new SimpleHttpValueProvider + ValueProvider = new SimpleValueProvider { { "someOtherName", "dummyValue" } }, @@ -238,7 +238,7 @@ public async Task ModelBinder_DoesNotFallBackToEmpty_IfErrorsAreAdded() ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List)), ModelName = "someName", ModelState = new ModelStateDictionary(), - ValueProvider = new SimpleHttpValueProvider + ValueProvider = new SimpleValueProvider { { "someOtherName", "dummyValue" } }, @@ -276,7 +276,7 @@ public async Task ModelBinder_ReturnsNotNull_SetsNullValue_SetsModelStateKey() ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List)), ModelName = "someName", ModelState = new ModelStateDictionary(), - ValueProvider = new SimpleHttpValueProvider + ValueProvider = new SimpleValueProvider { { "someOtherName", "dummyValue" } }, @@ -357,7 +357,7 @@ public async Task BindModel_WithDefaultBinders_BindsSimpleType() // Arrange var binder = CreateBinderWithDefaults(); - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "firstName", "firstName-value"}, { "lastName", "lastName-value"} @@ -400,7 +400,7 @@ public async Task BindModel_WithDefaultBinders_BindsComplexType() // Arrange var binder = CreateBinderWithDefaults(); - var valueProvider = new SimpleHttpValueProvider + var valueProvider = new SimpleValueProvider { { "firstName", "firstName-value"}, { "lastName", "lastName-value"}, @@ -436,7 +436,7 @@ public async Task BindModel_WithDefaultBinders_BindsComplexType() public async Task BindModel_DoesNotAddAValidationNode_IfModelIsNotSet() { // Arrange - var valueProvider = new SimpleHttpValueProvider(); + var valueProvider = new SimpleValueProvider(); var mockBinder = new Mock(); mockBinder .Setup(o => o.BindModelAsync(It.IsAny())) @@ -465,7 +465,7 @@ public async Task BindModel_DoesNotAddAValidationNode_IfModelBindingResultIsNull .Setup(o => o.BindModelAsync(It.IsAny())) .Returns(Task.FromResult(null)); var binder = CreateCompositeBinder(mockBinder.Object); - var valueProvider = new SimpleHttpValueProvider(); + var valueProvider = new SimpleValueProvider(); var bindingContext = CreateBindingContext(binder, valueProvider, typeof(SimplePropertiesModel)); // Act @@ -479,7 +479,7 @@ public async Task BindModel_DoesNotAddAValidationNode_IfModelBindingResultIsNull public async Task BindModel_UsesTheValidationNodeOnModelBindingResult_IfPresent() { // Arrange - var valueProvider = new SimpleHttpValueProvider(); + var valueProvider = new SimpleValueProvider(); ModelValidationNode validationNode = null; var mockBinder = new Mock(); @@ -531,10 +531,9 @@ private static CompositeModelBinder CreateBinderWithDefaults() { var binders = new IModelBinder[] { - new TypeMatchModelBinder(), new ByteArrayModelBinder(), new GenericModelBinder(), - new TypeConverterModelBinder(), + new SimpleTypeModelBinder(), new MutableObjectModelBinder() }; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryBasedValueProviderTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryBasedValueProviderTests.cs index bb58193a7e..a59d3b4af0 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryBasedValueProviderTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryBasedValueProviderTests.cs @@ -24,7 +24,7 @@ public async Task GetValueProvider_ReturnsNull_WhenKeyIsNotFound() var result = await provider.GetValueAsync("not-test-key"); // Assert - Assert.Null(result); + Assert.Equal(ValueProviderResult.None, result); } [Fact] @@ -41,7 +41,7 @@ public async Task GetValueProvider_ReturnsValue_IfKeyIsPresent() var result = await provider.GetValueAsync("test-key"); // Assert - Assert.Equal("test-value", result.RawValue); + Assert.Equal("test-value", (string)result); } [Fact] @@ -58,9 +58,7 @@ public async Task ContainsPrefixAsync_ReturnsNullValue_IfKeyIsPresent() var result = await provider.GetValueAsync("test-key"); // Assert - Assert.NotNull(result); - Assert.Null(result.RawValue); - Assert.Null(result.AttemptedValue); + Assert.Equal(string.Empty, (string)result); } [Theory] @@ -103,7 +101,7 @@ public async Task GetValueAsync_ReturnsCorrectValue_ForKnownKeys(string prefix, var result = await valueProvider.GetValueAsync(prefix); // Assert - Assert.Equal(expectedValue, (string)result.AttemptedValue); + Assert.Equal(expectedValue, (string)result); } [Fact] @@ -121,7 +119,7 @@ public async Task GetValueAsync_DoesNotReturnAValue_ForAKeyPrefix() var result = await valueProvider.GetValueAsync("bar"); // Assert - Assert.Null(result); + Assert.Equal(ValueProviderResult.None, result); } [Fact] diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs index 617bd67337..15f71c2eb9 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs @@ -21,7 +21,13 @@ public class DictionaryModelBinderTest public async Task BindModel_Succeeds(bool isReadOnly) { // Arrange - var bindingContext = GetModelBindingContext(isReadOnly); + var values = new Dictionary>() + { + { "someName[0]", new KeyValuePair(42, "forty-two") }, + { "someName[1]", new KeyValuePair(84, "eighty-four") }, + }; + + var bindingContext = GetModelBindingContext(isReadOnly, values); var modelState = bindingContext.ModelState; var binder = new DictionaryModelBinder(); @@ -43,10 +49,16 @@ public async Task BindModel_Succeeds(bool isReadOnly) [Theory] [InlineData(false)] [InlineData(true)] - public async Task BindModel_BindingContextModelNonNull_Succeeds(bool isReadOnly) + public async Task BindModel_WithExistingModel_Succeeds(bool isReadOnly) { // Arrange - var bindingContext = GetModelBindingContext(isReadOnly); + var values = new Dictionary>() + { + { "someName[0]", new KeyValuePair(42, "forty-two") }, + { "someName[1]", new KeyValuePair(84, "eighty-four") }, + }; + + var bindingContext = GetModelBindingContext(isReadOnly, values); var modelState = bindingContext.ModelState; var dictionary = new Dictionary(); bindingContext.Model = dictionary; @@ -430,8 +442,7 @@ private static IModelBinder CreateCompositeBinder() { var binders = new IModelBinder[] { - new TypeConverterModelBinder(), - new TypeMatchModelBinder(), + new SimpleTypeModelBinder(), new MutableObjectModelBinder(), }; @@ -465,49 +476,51 @@ private static IValueProvider CreateTestValueProvider(string keyFormat, IDiction return new TestValueProvider(BindingSource.Form, backingStore); } - private static ModelBindingContext GetModelBindingContext(bool isReadOnly) + private static ModelBindingContext GetModelBindingContext( + bool isReadOnly, + IDictionary> values) { var metadataProvider = new TestModelMetadataProvider(); metadataProvider.ForType>().BindingDetails(bd => bd.IsReadOnly = isReadOnly); - var valueProvider = new SimpleHttpValueProvider + + var binder = new Mock(); + binder + .Setup(mb => mb.BindModelAsync(It.IsAny())) + .Returns(mbc => + { + KeyValuePair value; + if (values.TryGetValue(mbc.ModelName, out value)) + { + return Task.FromResult(new ModelBindingResult(value, mbc.ModelName, isModelSet: true)); + } + else + { + return Task.FromResult(null); + } + }); + + var valueProvider = new SimpleValueProvider(); + foreach (var kvp in values) { - { "someName[0]", new KeyValuePair(42, "forty-two") }, - { "someName[1]", new KeyValuePair(84, "eighty-four") }, - }; + valueProvider.Add(kvp.Key, string.Empty); + } var bindingContext = new ModelBindingContext { ModelMetadata = metadataProvider.GetMetadataForType(typeof(IDictionary)), ModelName = "someName", - ValueProvider = valueProvider, OperationBindingContext = new OperationBindingContext { - ModelBinder = CreateKvpBinder(), - MetadataProvider = metadataProvider - } + ModelBinder = binder.Object, + MetadataProvider = metadataProvider, + ValueProvider = valueProvider, + }, + ValueProvider = valueProvider, }; return bindingContext; } - private static IModelBinder CreateKvpBinder() - { - Mock mockKvpBinder = new Mock(); - mockKvpBinder - .Setup(o => o.BindModelAsync(It.IsAny())) - .Returns(async (ModelBindingContext mbc) => - { - var value = await mbc.ValueProvider.GetValueAsync(mbc.ModelName); - if (value != null) - { - var model = value.ConvertTo(mbc.ModelType); - return new ModelBindingResult(model, key: null, isModelSet: true); - } - return null; - }); - return mockKvpBinder.Object; - } - private class ModelWithDictionaryProperties { // A Dictionary instance cannot be assigned to this property. diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ElementalValueProviderTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ElementalValueProviderTests.cs index 0b48048c71..b6c4c7ff5b 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ElementalValueProviderTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ElementalValueProviderTests.cs @@ -19,9 +19,10 @@ public async Task ContainsPrefixAsync_ReturnsTrue_IfElementNameStartsWithPrefix( { // Arrange var culture = new CultureInfo("en-US"); - var elementalValueProvider = new ElementalValueProvider(elementName, - new object(), - culture); + var elementalValueProvider = new ElementalValueProvider( + elementName, + "hi", + culture); // Act var containsPrefix = await elementalValueProvider.ContainsPrefixAsync(prefix); @@ -39,9 +40,10 @@ public async Task ContainsPrefixAsync_ReturnsFalse_IfElementCannotSpecifyValuesF { // Arrange var culture = new CultureInfo("en-US"); - var elementalValueProvider = new ElementalValueProvider(elementName, - new object(), - culture); + var elementalValueProvider = new ElementalValueProvider( + elementName, + "hi", + culture); // Act var containsPrefix = await elementalValueProvider.ContainsPrefixAsync(prefix); @@ -51,18 +53,17 @@ public async Task ContainsPrefixAsync_ReturnsFalse_IfElementCannotSpecifyValuesF } [Fact] - public async Task GetValueAsync_NameDoesNotMatch_ReturnsNull() + public async Task GetValueAsync_NameDoesNotMatch_ReturnsEmptyResult() { // Arrange var culture = new CultureInfo("fr-FR"); - var rawValue = new DateTime(2001, 1, 2); - var valueProvider = new ElementalValueProvider("foo", rawValue, culture); + var valueProvider = new ElementalValueProvider("foo", "hi", culture); // Act - var vpResult = await valueProvider.GetValueAsync("bar"); + var result = await valueProvider.GetValueAsync("bar"); // Assert - Assert.Null(vpResult); + Assert.Equal(ValueProviderResult.None, result); } [Theory] @@ -73,17 +74,15 @@ public async Task GetValueAsync_NameMatches_ReturnsValueProviderResult(string na { // Arrange var culture = new CultureInfo("fr-FR"); - var rawValue = new DateTime(2001, 1, 2); - var valueProvider = new ElementalValueProvider("foo", rawValue, culture); + var valueProvider = new ElementalValueProvider("foo", "hi", culture); // Act - var vpResult = await valueProvider.GetValueAsync(name); + var result = await valueProvider.GetValueAsync(name); // Assert - Assert.NotNull(vpResult); - Assert.Equal(rawValue, vpResult.RawValue); - Assert.Equal("02/01/2001 00:00:00", vpResult.AttemptedValue); - Assert.Equal(culture, vpResult.Culture); + Assert.NotNull(result); + Assert.Equal("hi", (string)result); + Assert.Equal(culture, result.Culture); } } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/EnumerableValueProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/EnumerableValueProviderTest.cs index 9fdaf664f6..ea635be0b7 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/EnumerableValueProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/EnumerableValueProviderTest.cs @@ -167,8 +167,7 @@ public async Task GetValueAsync_SingleValue() // Assert Assert.NotNull(result); - Assert.Equal("someOtherValue", result.RawValue); - Assert.Equal("someOtherValue", result.AttemptedValue); + Assert.Equal("someOtherValue", (string)result); Assert.Equal(culture, result.Culture); } @@ -184,8 +183,8 @@ public async Task GetValueAsync_MultiValue() // Assert Assert.NotNull(result); - Assert.Equal(new[] { "someValue1", "someValue2" }, (IList)result.RawValue); - Assert.Equal("someValue1,someValue2", result.AttemptedValue); + Assert.Equal(new[] { "someValue1", "someValue2" }, result.Values); + Assert.Equal("someValue1,someValue2", (string)result); Assert.Equal(culture, result.Culture); } @@ -202,7 +201,7 @@ public async Task GetValue_NullValue(string key) var result = await valueProvider.GetValueAsync(key); // Assert - Assert.Null(result); + Assert.Equal(ValueProviderResult.None, result); } [Fact] @@ -220,8 +219,8 @@ public async Task GetValueAsync_NullMultipleValue() var result = await valueProvider.GetValueAsync("key"); // Assert - Assert.Equal(new[] { null, null, "value" }, result.RawValue as IEnumerable); - Assert.Equal(",,value", result.AttemptedValue); + Assert.Equal(new[] { null, null, "value" }, result.Values); + Assert.Equal(",,value", (string)result); } [Fact] @@ -234,7 +233,7 @@ public async Task GetValueAsync_ReturnsNullIfKeyNotFound() var result = await valueProvider.GetValueAsync("prefix"); // Assert - Assert.Null(result); + Assert.Equal(ValueProviderResult.None, result); } [Fact] diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs index c95b2691d5..e9de57468e 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs @@ -110,10 +110,7 @@ public async Task GetValueProvider_ReturnsValueProvider_ContainingExpectedKeys(s var result = await valueProvider.GetValueAsync(key); // Assert - Assert.NotNull(result); - Assert.NotNull(result.RawValue); - var value = Assert.IsType(result.RawValue); - Assert.Equal("found", value); + Assert.Equal("found", (string)result); } private static ValueProviderFactoryContext CreateContext( diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs index df1c2ef042..7f70c3bdc8 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs @@ -19,7 +19,7 @@ public class KeyValuePairModelBinderTest public async Task BindModel_MissingKey_ReturnsResult_AndAddsModelValidationError() { // Arrange - var valueProvider = new SimpleHttpValueProvider(); + var valueProvider = new SimpleValueProvider(); // Create string binder to create the value but not the key. var bindingContext = GetBindingContext(valueProvider, CreateStringBinder()); @@ -42,7 +42,7 @@ public async Task BindModel_MissingKey_ReturnsResult_AndAddsModelValidationError public async Task BindModel_MissingValue_ReturnsResult_AndAddsModelValidationError() { // Arrange - var valueProvider = new SimpleHttpValueProvider(); + var valueProvider = new SimpleValueProvider(); // Create int binder to create the value but not the key. var bindingContext = GetBindingContext(valueProvider, CreateIntBinder()); @@ -64,7 +64,7 @@ public async Task BindModel_MissingValue_ReturnsResult_AndAddsModelValidationErr public async Task BindModel_MissingKeyAndMissingValue_DoNotAddModelStateError() { // Arrange - var valueProvider = new SimpleHttpValueProvider(); + var valueProvider = new SimpleValueProvider(); // Create int binder to create the value but not the key. var bindingContext = GetBindingContext(valueProvider); @@ -89,7 +89,7 @@ public async Task BindModel_SubBindingSucceeds() { // Arrange var innerBinder = new CompositeModelBinder(new[] { CreateStringBinder(), CreateIntBinder() }); - var valueProvider = new SimpleHttpValueProvider(); + var valueProvider = new SimpleValueProvider(); var bindingContext = GetBindingContext(valueProvider, innerBinder); var binder = new KeyValuePairModelBinder(); @@ -134,7 +134,7 @@ public async Task TryBindStrongModel_InnerBinderReturnsNotNull_ReturnsInnerBinde Assert.Equal("someName.key", context.ModelName); return Task.FromResult(innerResult); }); - var bindingContext = GetBindingContext(new SimpleHttpValueProvider(), innerBinder.Object); + var bindingContext = GetBindingContext(new SimpleValueProvider(), innerBinder.Object); var binder = new KeyValuePairModelBinder(); var modelValidationNodeList = new List(); @@ -238,7 +238,7 @@ private static ModelBindingContext CreateContext() { HttpContext = new DefaultHttpContext(), MetadataProvider = new TestModelMetadataProvider(), - ModelBinder = new TypeMatchModelBinder(), + ModelBinder = new SimpleTypeModelBinder(), } }; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ModelBindingContextTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ModelBindingContextTest.cs index 1976882095..e12e8aac16 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ModelBindingContextTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ModelBindingContextTest.cs @@ -18,7 +18,7 @@ public void GetChildModelBindingContext() ModelMetadata = new TestModelMetadataProvider().GetMetadataForType(typeof(object)), ModelName = "theName", ModelState = new ModelStateDictionary(), - ValueProvider = new SimpleHttpValueProvider() + ValueProvider = new SimpleValueProvider() }; var metadataProvider = new TestModelMetadataProvider(); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/TypeConverterModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleTypeModelBinderTest.cs similarity index 83% rename from test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/TypeConverterModelBinderTest.cs rename to test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleTypeModelBinderTest.cs index b0a0a51abe..bd210613c3 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/TypeConverterModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleTypeModelBinderTest.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test { - public class TypeConverterModelBinderTest + public class SimpleTypeModelBinderTest { [Theory] [InlineData(typeof(object))] @@ -19,12 +19,12 @@ public async Task BindModel_ReturnsNull_IfTypeCannotBeConverted(Type destination { // Arrange var bindingContext = GetBindingContext(destinationType); - bindingContext.ValueProvider = new SimpleHttpValueProvider + bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "some-value" } }; - var binder = new TypeConverterModelBinder(); + var binder = new SimpleTypeModelBinder(); // Act var retVal = await binder.BindModelAsync(bindingContext); @@ -53,12 +53,12 @@ public async Task BindModel_ReturnsNotNull_IfTypeCanBeConverted(Type destination // Arrange var bindingContext = GetBindingContext(destinationType); - bindingContext.ValueProvider = new SimpleHttpValueProvider + bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "some-value" } }; - var binder = new TypeConverterModelBinder(); + var binder = new SimpleTypeModelBinder(); // Act var retVal = await binder.BindModelAsync(bindingContext); @@ -79,11 +79,11 @@ public async Task BindModel_CreatesError_WhenTypeConversionIsNull(Type destinati { // Arrange var bindingContext = GetBindingContext(destinationType); - bindingContext.ValueProvider = new SimpleHttpValueProvider + bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", string.Empty } }; - var binder = new TypeConverterModelBinder(); + var binder = new SimpleTypeModelBinder(); // Act var result = await binder.BindModelAsync(bindingContext); @@ -104,19 +104,19 @@ public async Task BindModel_Error_FormatExceptionsTurnedIntoStringsInModelState( // Arrange var message = "The value 'not an integer' is not valid for theModelName."; var bindingContext = GetBindingContext(typeof(int)); - bindingContext.ValueProvider = new SimpleHttpValueProvider + bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "not an integer" } }; - var binder = new TypeConverterModelBinder(); + var binder = new SimpleTypeModelBinder(); // Act - var retVal = await binder.BindModelAsync(bindingContext); + var result = await binder.BindModelAsync(bindingContext); // Assert - Assert.NotNull(retVal); - Assert.Null(retVal.Model); + Assert.NotNull(result); + Assert.Null(result.Model); Assert.False(bindingContext.ModelState.IsValid); var error = Assert.Single(bindingContext.ModelState["theModelName"].Errors); Assert.Equal(message, error.ErrorMessage); @@ -127,7 +127,7 @@ public async Task BindModel_NullValueProviderResult_ReturnsNull() { // Arrange var bindingContext = GetBindingContext(typeof(int)); - var binder = new TypeConverterModelBinder(); + var binder = new SimpleTypeModelBinder(); // Act var retVal = await binder.BindModelAsync(bindingContext); @@ -142,12 +142,12 @@ public async Task BindModel_ValidValueProviderResult_ConvertEmptyStringsToNull() { // Arrange var bindingContext = GetBindingContext(typeof(string)); - bindingContext.ValueProvider = new SimpleHttpValueProvider + bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", string.Empty } }; - var binder = new TypeConverterModelBinder(); + var binder = new SimpleTypeModelBinder(); // Act var retVal = await binder.BindModelAsync(bindingContext); @@ -163,12 +163,12 @@ public async Task BindModel_ValidValueProviderResult_ReturnsModel() { // Arrange var bindingContext = GetBindingContext(typeof(int)); - bindingContext.ValueProvider = new SimpleHttpValueProvider + bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "42" } }; - var binder = new TypeConverterModelBinder(); + var binder = new SimpleTypeModelBinder(); // Act var retVal = await binder.BindModelAsync(bindingContext); @@ -185,7 +185,7 @@ private static ModelBindingContext GetBindingContext(Type modelType) { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType), ModelName = "theModelName", - ValueProvider = new SimpleHttpValueProvider() // empty + ValueProvider = new SimpleValueProvider() // empty }; } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleHttpValueProvider.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleValueProvider.cs similarity index 66% rename from test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleHttpValueProvider.cs rename to test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleValueProvider.cs index a512615540..1570b22b42 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleHttpValueProvider.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleValueProvider.cs @@ -8,16 +8,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test { - public sealed class SimpleHttpValueProvider : Dictionary, IValueProvider + public sealed class SimpleValueProvider : Dictionary, IValueProvider { private readonly CultureInfo _culture; - public SimpleHttpValueProvider() + public SimpleValueProvider() : this(null) { } - public SimpleHttpValueProvider(CultureInfo culture) + public SimpleValueProvider(CultureInfo culture) : base(StringComparer.OrdinalIgnoreCase) { _culture = culture ?? CultureInfo.InvariantCulture; @@ -59,11 +59,28 @@ public Task ContainsPrefixAsync(string prefix) public Task GetValueAsync(string key) { - ValueProviderResult result = null; + ValueProviderResult result = ValueProviderResult.None; + object rawValue; if (TryGetValue(key, out rawValue)) { - result = new ValueProviderResult(rawValue, Convert.ToString(rawValue, _culture), _culture); + if (rawValue != null && rawValue.GetType().IsArray) + { + var array = (Array)rawValue; + + var stringValues = new string[array.Length]; + for (var i = 0; i < array.Length; i++) + { + stringValues[i] = array.GetValue(i) as string ?? Convert.ToString(array.GetValue(i), _culture); + } + + result = new ValueProviderResult(stringValues, _culture); + } + else + { + var stringValue = rawValue as string ?? Convert.ToString(rawValue, _culture) ?? string.Empty; + result = new ValueProviderResult(stringValue, _culture); + } } return Task.FromResult(result); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/TypeMatchModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/TypeMatchModelBinderTest.cs deleted file mode 100644 index 0497e8ea9a..0000000000 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/TypeMatchModelBinderTest.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. See License.txt in the project root for license information. - -using System; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.AspNet.Mvc.ModelBinding.Test -{ - public class TypeMatchModelBinderTest - { - [Fact] - public async Task BindModel_InvalidValueProviderResult_ReturnsNull() - { - // Arrange - var bindingContext = GetBindingContext(); - bindingContext.ValueProvider = new SimpleHttpValueProvider - { - { "theModelName", "not an integer" } - }; - - var binder = new TypeMatchModelBinder(); - var modelState = bindingContext.ModelState; - - // Act - var result = await binder.BindModelAsync(bindingContext); - - // Assert - Assert.Null(result); - Assert.Empty(modelState.Keys); - Assert.True(modelState.IsValid); - } - - [Fact] - public async Task BindModel_ValidValueProviderResult_ReturnsNotNull() - { - // Arrange - var bindingContext = GetBindingContext(); - bindingContext.ValueProvider = new SimpleHttpValueProvider - { - { "theModelName", 42 } - }; - - var binder = new TypeMatchModelBinder(); - var modelState = bindingContext.ModelState; - - // Act - var result = await binder.BindModelAsync(bindingContext); - - // Assert - Assert.NotNull(result); - Assert.True(result.IsModelSet); - Assert.Equal(42, result.Model); - var key = Assert.Single(modelState.Keys); - Assert.Equal("theModelName", key); - Assert.Equal("42", modelState[key].Value.AttemptedValue); - Assert.Equal(42, modelState[key].Value.RawValue); - } - - [Fact] - public async Task GetCompatibleValueProviderResult_ValueProviderResultRawValueIncorrect_ReturnsNull() - { - // Arrange - var bindingContext = GetBindingContext(); - bindingContext.ValueProvider = new SimpleHttpValueProvider - { - { "theModelName", "not an integer" } - }; - - // Act - var vpResult = await TypeMatchModelBinder.GetCompatibleValueProviderResult(bindingContext); - - // Assert - Assert.Null(vpResult); // Raw value is the wrong type - } - - [Fact] - public async Task GetCompatibleValueProviderResult_ValueProviderResultValid_ReturnsValueProviderResult() - { - // Arrange - var bindingContext = GetBindingContext(); - bindingContext.ValueProvider = new SimpleHttpValueProvider - { - { "theModelName", 42 } - }; - - // Act - var vpResult = await TypeMatchModelBinder.GetCompatibleValueProviderResult(bindingContext); - - // Assert - Assert.NotNull(vpResult); - } - - [Fact] - public async Task GetCompatibleValueProviderResult_ValueProviderReturnsNull_ReturnsNull() - { - // Arrange - var bindingContext = GetBindingContext(); - bindingContext.ValueProvider = new SimpleHttpValueProvider(); - - // Act - var vpResult = await TypeMatchModelBinder.GetCompatibleValueProviderResult(bindingContext); - - // Assert - Assert.Null(vpResult); // No key matched - } - - private static ModelBindingContext GetBindingContext() - { - return GetBindingContext(typeof(int)); - } - - private static ModelBindingContext GetBindingContext(Type modelType) - { - return new ModelBindingContext - { - ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType), - ModelName = "theModelName" - }; - } - } -} diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/Validation/DefaultObjectValidatorTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/Validation/DefaultObjectValidatorTests.cs index 3ccac28d46..8a791b5672 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/Validation/DefaultObjectValidatorTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/Validation/DefaultObjectValidatorTests.cs @@ -495,8 +495,7 @@ public void ForExcludedModelBoundTypes_Properties_MarkedAsSkipped() var validationContext = testValidationContext.ModelValidationContext; // Set the value on model state as a model binder would. - var valueProviderResult = new ValueProviderResult(rawValue: "password"); - validationContext.ModelState.SetModelValue("user.Password", valueProviderResult); + validationContext.ModelState.SetModelValue("user.Password", new string[] { "password" }, "password"); var validator = new DefaultObjectValidator( testValidationContext.ExcludeFilters, testValidationContext.ModelMetadataProvider); @@ -517,7 +516,8 @@ public void ForExcludedModelBoundTypes_Properties_MarkedAsSkipped() Assert.Equal("user.Password", entry.Key); Assert.Empty(entry.Value.Errors); Assert.Equal(entry.Value.ValidationState, ModelValidationState.Skipped); - Assert.Same(valueProviderResult, entry.Value.Value); + Assert.Equal(new string[] { "password" }, entry.Value.RawValue); + Assert.Same("password", entry.Value.AttemptedValue); } private class Person2 diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBindingHelperTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBindingHelperTest.cs index e3f643bb19..3ce93de31d 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBindingHelperTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBindingHelperTest.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Globalization; using System.Linq.Expressions; using System.Threading.Tasks; using Microsoft.AspNet.Http; @@ -68,7 +69,7 @@ public async Task TryUpdateModel_ReturnsFalse_IfModelValidationFails() var expectedMessage = PlatformNormalizer.NormalizeContent("The MyProperty field is required."); var binders = new IModelBinder[] { - new TypeConverterModelBinder(), + new SimpleTypeModelBinder(), new MutableObjectModelBinder() }; @@ -107,7 +108,7 @@ public async Task TryUpdateModel_ReturnsTrue_IfModelBindsAndValidatesSuccessfull // Arrange var binders = new IModelBinder[] { - new TypeConverterModelBinder(), + new SimpleTypeModelBinder(), new MutableObjectModelBinder() }; @@ -181,7 +182,7 @@ public async Task TryUpdateModel_UsingIncludePredicateOverload_ReturnsTrue_Model // Arrange var binders = new IModelBinder[] { - new TypeConverterModelBinder(), + new SimpleTypeModelBinder(), new MutableObjectModelBinder() }; @@ -270,7 +271,7 @@ public async Task TryUpdateModel_UsingIncludeExpressionOverload_ReturnsTrue_Mode // Arrange var binders = new IModelBinder[] { - new TypeConverterModelBinder(), + new SimpleTypeModelBinder(), new MutableObjectModelBinder() }; @@ -322,7 +323,7 @@ public async Task TryUpdateModel_UsingDefaultIncludeOverload_IncludesAllProperti // Arrange var binders = new IModelBinder[] { - new TypeConverterModelBinder(), + new SimpleTypeModelBinder(), new MutableObjectModelBinder() }; @@ -528,7 +529,7 @@ public async Task TryUpdateModelNonGeneric_PredicateOverload_ReturnsTrue_ModelBi // Arrange var binders = new IModelBinder[] { - new TypeConverterModelBinder(), + new SimpleTypeModelBinder(), new MutableObjectModelBinder() }; @@ -619,7 +620,7 @@ public async Task TryUpdateModelNonGeneric_ModelTypeOverload_ReturnsTrue_IfModel // Arrange var binders = new IModelBinder[] { - new TypeConverterModelBinder(), + new SimpleTypeModelBinder(), new MutableObjectModelBinder() }; @@ -876,6 +877,514 @@ public class Order public string Name { get; set; } public Address Address { get; set; } } + + [Fact] + public void ConvertTo_ReturnsNullForReferenceTypes_WhenValueIsNull() + { + var convertedValue = ModelBindingHelper.ConvertTo(null, typeof(string)); + Assert.Null(convertedValue); + } + + [Fact] + public void ConvertTo_ReturnsDefaultForValueTypes_WhenValueIsNull() + { + var convertedValue = ModelBindingHelper.ConvertTo(null, typeof(int)); + Assert.Equal(0, convertedValue); + } + + [Fact] + public void ConvertToCanConvertArraysToSingleElements() + { + // Arrange + var value = new int[] { 1, 20, 42 }; + + // Act + var converted = ModelBindingHelper.ConvertTo(value, typeof(string)); + + // Assert + Assert.Equal("1", converted); + } + + [Fact] + public void ConvertToCanConvertSingleElementsToArrays() + { + // Arrange + var value = 42; + + // Act + var converted = ModelBindingHelper.ConvertTo(value); + + // Assert + Assert.NotNull(converted); + var result = Assert.Single(converted); + Assert.Equal("42", result); + } + + [Fact] + public void ConvertToCanConvertSingleElementsToSingleElements() + { + // Arrange + + // Act + var converted = ModelBindingHelper.ConvertTo(42); + + // Assert + Assert.NotNull(converted); + Assert.Equal("42", converted); + } + + [Fact] + public void ConvertingNullStringToNullableIntReturnsNull() + { + // Arrange + + // Act + var returned = ModelBindingHelper.ConvertTo(null); + + // Assert + Assert.Equal(returned, null); + } + + [Fact] + public void ConvertingWhiteSpaceStringToNullableIntReturnsNull() + { + // Arrange + var original = " "; + + // Act + var returned = ModelBindingHelper.ConvertTo(original); + + // Assert + Assert.Equal(returned, null); + } + + [Fact] + public void ConvertToReturnsNullIfArrayElementValueIsNull() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(new string[] { null }, typeof(int)); + + // Assert + Assert.Null(outValue); + } + + [Fact] + public void ConvertToReturnsNullIfTryingToConvertEmptyArrayToSingleElement() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(new int[0], typeof(int)); + + // Assert + Assert.Null(outValue); + } + + [Theory] + [InlineData("")] + [InlineData(" \t \r\n ")] + public void ConvertToReturnsNullIfTrimmedValueIsEmptyString(object value) + { + // Arrange + // Act + var outValue = ModelBindingHelper.ConvertTo(value, typeof(int)); + + // Assert + Assert.Null(outValue); + } + + [Fact] + public void ConvertToReturnsNullIfTrimmedValueIsEmptyString() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(null, typeof(int[])); + + // Assert + Assert.Null(outValue); + } + + [Fact] + public void ConvertToReturnsValueIfArrayElementIsIntegerAndDestinationTypeIsEnum() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(new object[] { 1 }, typeof(IntEnum)); + + // Assert + Assert.Equal(outValue, IntEnum.Value1); + } + + [Theory] + [InlineData(1, typeof(IntEnum), IntEnum.Value1)] + [InlineData(1L, typeof(LongEnum), LongEnum.Value1)] + [InlineData(long.MaxValue, typeof(LongEnum), LongEnum.MaxValue)] + [InlineData(1U, typeof(UnsignedIntEnum), UnsignedIntEnum.Value1)] + [InlineData(1UL, typeof(IntEnum), IntEnum.Value1)] + [InlineData((byte)1, typeof(ByteEnum), ByteEnum.Value1)] + [InlineData(byte.MaxValue, typeof(ByteEnum), ByteEnum.MaxValue)] + [InlineData((sbyte)1, typeof(ByteEnum), ByteEnum.Value1)] + [InlineData((short)1, typeof(IntEnum), IntEnum.Value1)] + [InlineData((ushort)1, typeof(IntEnum), IntEnum.Value1)] + [InlineData(int.MaxValue, typeof(IntEnum?), IntEnum.MaxValue)] + [InlineData(null, typeof(IntEnum?), null)] + [InlineData(1L, typeof(LongEnum?), LongEnum.Value1)] + [InlineData(null, typeof(LongEnum?), null)] + [InlineData(uint.MaxValue, typeof(UnsignedIntEnum?), UnsignedIntEnum.MaxValue)] + [InlineData((byte)1, typeof(ByteEnum?), ByteEnum.Value1)] + [InlineData(null, typeof(ByteEnum?), null)] + [InlineData((ushort)1, typeof(LongEnum?), LongEnum.Value1)] + public void ConvertToReturnsValueIfArrayElementIsAnyIntegerTypeAndDestinationTypeIsEnum( + object input, + Type enumType, + object expected) + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(new object[] { input }, enumType); + + // Assert + Assert.Equal(expected, outValue); + } + + [Fact] + public void ConvertToReturnsValueIfArrayElementIsStringValueAndDestinationTypeIsEnum() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(new object[] { "1" }, typeof(IntEnum)); + + // Assert + Assert.Equal(outValue, IntEnum.Value1); + } + + [Fact] + public void ConvertToReturnsValueIfArrayElementIsStringKeyAndDestinationTypeIsEnum() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(new object[] { "Value1" }, typeof(IntEnum)); + + // Assert + Assert.Equal(outValue, IntEnum.Value1); + } + + [Fact] + public void ConvertToReturnsValueIfElementIsStringAndDestinationIsNullableInteger() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo("12", typeof(int?)); + + // Assert + Assert.Equal(12, outValue); + } + + [Fact] + public void ConvertToReturnsValueIfElementIsStringAndDestinationIsNullableDouble() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo("12.5", typeof(double?)); + + // Assert + Assert.Equal(12.5, outValue); + } + + [Fact] + public void ConvertToReturnsValueIfElementIsDecimalAndDestinationIsNullableInteger() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(12M, typeof(int?)); + + // Assert + Assert.Equal(12, outValue); + } + + [Fact] + public void ConvertToReturnsValueIfElementIsDecimalAndDestinationIsNullableDouble() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(12.5M, typeof(double?)); + + // Assert + Assert.Equal(12.5, outValue); + } + + [Fact] + public void ConvertToReturnsValueIfElementIsDecimalDoubleAndDestinationIsNullableInteger() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(12M, typeof(int?)); + + // Assert + Assert.Equal(12, outValue); + } + + [Fact] + public void ConvertToReturnsValueIfElementIsDecimalDoubleAndDestinationIsNullableLong() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(12M, typeof(long?)); + + // Assert + Assert.Equal(12L, outValue); + } + + [Fact] + public void ConvertToReturnsValueIfArrayElementInstanceOfDestinationType() + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(new object[] { "some string" }, typeof(string)); + + // Assert + Assert.Equal("some string", outValue); + } + + [Theory] + [InlineData(new object[] { new[] { 1, 0 } })] + [InlineData(new object[] { new[] { "Value1", "Value0" } })] + [InlineData(new object[] { new[] { "Value1", "value0" } })] + public void ConvertTo_ConvertsEnumArrays(object value) + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(value, typeof(IntEnum[])); + + // Assert + var result = Assert.IsType(outValue); + Assert.Equal(2, result.Length); + Assert.Equal(IntEnum.Value1, result[0]); + Assert.Equal(IntEnum.Value0, result[1]); + } + + [Theory] + [InlineData(new object[] { new[] { 1, 2 }, new[] { FlagsEnum.Value1, FlagsEnum.Value2 } })] + [InlineData(new object[] { new[] { "Value1", "Value2" }, new[] { FlagsEnum.Value1, FlagsEnum.Value2 } })] + [InlineData(new object[] { new[] { 5, 2 }, new[] { FlagsEnum.Value1 | FlagsEnum.Value4, FlagsEnum.Value2 } })] + public void ConvertTo_ConvertsFlagsEnumArrays(object value, FlagsEnum[] expected) + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(value, typeof(FlagsEnum[])); + + // Assert + var result = Assert.IsType(outValue); + Assert.Equal(2, result.Length); + Assert.Equal(expected[0], result[0]); + Assert.Equal(expected[1], result[1]); + } + + [Fact] + public void ConvertToReturnsValueIfInstanceOfDestinationType() + { + // Arrange + var original = new[] { "some string" }; + + // Act + var outValue = ModelBindingHelper.ConvertTo(original, typeof(string[])); + + // Assert + Assert.Same(original, outValue); + } + + [Theory] + [InlineData(typeof(int))] + [InlineData(typeof(double?))] + [InlineData(typeof(IntEnum?))] + public void ConvertToThrowsIfConverterThrows(Type destinationType) + { + // Arrange + + // Act & Assert + var ex = Assert.Throws( + () => ModelBindingHelper.ConvertTo("this-is-not-a-valid-value", destinationType)); + } + + [Fact] + public void ConvertToThrowsIfNoConverterExists() + { + // Arrange + var destinationType = typeof(MyClassWithoutConverter); + + // Act & Assert + var ex = Assert.Throws( + () => ModelBindingHelper.ConvertTo("x", destinationType)); + Assert.Equal("The parameter conversion from type 'System.String' to type " + + $"'{typeof(MyClassWithoutConverter).FullName}' " + + "failed because no type converter can convert between these types.", + ex.Message); + } + + [Fact] + public void ConvertToUsesProvidedCulture() + { + // Arrange + + // Act + var cultureResult = ModelBindingHelper.ConvertTo("12,5", typeof(decimal), new CultureInfo("fr-FR")); + + // Assert + Assert.Equal(12.5M, cultureResult); + Assert.Throws( + () => ModelBindingHelper.ConvertTo("12,5", typeof(decimal), new CultureInfo("en-GB"))); + } + + [Theory] + [MemberData(nameof(IntrinsicConversionData))] + public void ConvertToCanConvertIntrinsics(object initialValue, T expectedValue) + { + // Arrange + + // Act & Assert + Assert.Equal(expectedValue, ModelBindingHelper.ConvertTo(initialValue, typeof(T))); + } + + public static IEnumerable IntrinsicConversionData + { + get + { + yield return new object[] { 42, 42L }; + yield return new object[] { 42, (short)42 }; + yield return new object[] { 42, (float)42.0 }; + yield return new object[] { 42, (double)42.0 }; + yield return new object[] { 42M, 42 }; + yield return new object[] { 42L, 42 }; + yield return new object[] { 42, (byte)42 }; + yield return new object[] { (short)42, 42 }; + yield return new object[] { (float)42.0, 42 }; + yield return new object[] { (double)42.0, 42 }; + yield return new object[] { (byte)42, 42 }; + yield return new object[] { "2008-01-01", new DateTime(2008, 01, 01) }; + yield return new object[] { "00:00:20", TimeSpan.FromSeconds(20) }; + yield return new object[] + { + "c6687d3a-51f9-4159-8771-a66d2b7d7038", + Guid.Parse("c6687d3a-51f9-4159-8771-a66d2b7d7038") + }; + } + } + + [Theory] + [InlineData(typeof(TimeSpan))] + [InlineData(typeof(DateTime))] + [InlineData(typeof(DateTimeOffset))] + [InlineData(typeof(Guid))] + [InlineData(typeof(IntEnum))] + public void ConvertTo_Throws_IfValueIsNotStringData(Type destinationType) + { + // Arrange + + // Act + var ex = Assert.Throws( + () => ModelBindingHelper.ConvertTo(new MyClassWithoutConverter(), destinationType)); + + // Assert + var expectedMessage = string.Format("The parameter conversion from type '{0}' to type '{1}' " + + "failed because no type converter can convert between these types.", + typeof(MyClassWithoutConverter), destinationType); + Assert.Equal(expectedMessage, ex.Message); + } + + [Fact] + public void ConvertTo_Throws_IfDestinationTypeIsNotConvertible() + { + // Arrange + var value = "Hello world"; + var destinationType = typeof(MyClassWithoutConverter); + + // Act + var ex = Assert.Throws( + () => ModelBindingHelper.ConvertTo(value, destinationType)); + + // Assert + var expectedMessage = string.Format("The parameter conversion from type '{0}' to type '{1}' " + + "failed because no type converter can convert between these types.", + value.GetType(), typeof(MyClassWithoutConverter)); + Assert.Equal(expectedMessage, ex.Message); + } + + [Theory] + [InlineData(new object[] { 2, FlagsEnum.Value2 })] + [InlineData(new object[] { 5, FlagsEnum.Value1 | FlagsEnum.Value4 })] + [InlineData(new object[] { 15, FlagsEnum.Value1 | FlagsEnum.Value2 | FlagsEnum.Value4 | FlagsEnum.Value8 })] + [InlineData(new object[] { 16, (FlagsEnum)16 })] + [InlineData(new object[] { 0, (FlagsEnum)0 })] + [InlineData(new object[] { null, (FlagsEnum)0 })] + [InlineData(new object[] { "Value1,Value2", (FlagsEnum)3 })] + [InlineData(new object[] { "Value1,Value2,value4, value8", (FlagsEnum)15 })] + public void ConvertTo_ConvertsEnumFlags(object value, object expected) + { + // Arrange + + // Act + var outValue = ModelBindingHelper.ConvertTo(value); + + // Assert + Assert.Equal(expected, outValue); + } + + private class MyClassWithoutConverter + { + } + + private enum IntEnum + { + Value0 = 0, + Value1 = 1, + MaxValue = int.MaxValue + } + + private enum LongEnum : long + { + Value0 = 0L, + Value1 = 1L, + MaxValue = long.MaxValue + } + + private enum UnsignedIntEnum : uint + { + Value0 = 0U, + Value1 = 1U, + MaxValue = uint.MaxValue + } + + private enum ByteEnum : byte + { + Value0 = 0, + Value1 = 1, + MaxValue = byte.MaxValue + } + + [Flags] + public enum FlagsEnum + { + Value1 = 1, + Value2 = 2, + Value4 = 4, + Value8 = 8 + } } } #endif diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/SerializableErrorTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/SerializableErrorTests.cs index 04011aa9dc..e56bdd5768 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/SerializableErrorTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/SerializableErrorTests.cs @@ -72,10 +72,10 @@ public void DoesNotAddEntries_IfNoErrorsArePresent() var modelState = new ModelStateDictionary(); modelState.Add( "key1", - new ModelState() { Value = new ValueProviderResult("foo", "foo", CultureInfo.InvariantCulture) }); + new ModelState()); modelState.Add( "key2", - new ModelState() { Value = new ValueProviderResult("bar", "bar", CultureInfo.InvariantCulture) }); + new ModelState()); // Act var serializableError = new SerializableError(modelState); diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs index 696f366d9d..0fbe0999c9 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs @@ -2086,13 +2086,7 @@ public async Task BindModelAsync_WithIncorrectlyFormattedNestedCollectionValue_B // Assert var result = await ReadValue(response); - - // Though posted content did not contain any valid Addresses, it is bound as a single-element List - // containing null. Slightly odd behavior is specific to this unusual error case: CollectionModelBinder - // attempted to bind a comma-separated string as a collection and Address lacks a from-string conversion. - // MutableObjectModelBinder does not create model when value providers have no data (unless at top level). - var address = Assert.Single(result.Addresses); - Assert.Null(address); + Assert.Empty(result.Addresses); } [Fact] @@ -2153,12 +2147,7 @@ public async Task // Assert var result = await ReadValue(response); - // Though posted content did not contain any valid People, it is bound as a single-element List - // containing null. Slightly odd behavior is specific to this unusual error case: CollectionModelBinder - // attempted to bind a comma-separated string as a collection and Address lacks a from-string conversion. - // MutableObjectModelBinder does not create model when value providers have no data (unless at top level). - var person = Assert.Single(result.People); - Assert.Null(person); + Assert.Empty(result.People); } [Theory] diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/ActionParametersIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/ActionParametersIntegrationTest.cs index a691f6fca7..a789f6a8a6 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/ActionParametersIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/ActionParametersIntegrationTest.cs @@ -65,9 +65,8 @@ public async Task ActionParameter_NonSettableCollectionModel_EmptyPrefix_GetsBou Assert.Equal(1, modelState.Keys.Count); var key = Assert.Single(modelState.Keys, k => k == "Address[0].Street"); - Assert.NotNull(modelState[key].Value); - Assert.Equal("SomeStreet", modelState[key].Value.AttemptedValue); - Assert.Equal("SomeStreet", modelState[key].Value.RawValue); + Assert.Equal("SomeStreet", modelState[key].AttemptedValue); + Assert.Equal("SomeStreet", modelState[key].RawValue); Assert.Empty(modelState[key].Errors); Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); } @@ -116,7 +115,8 @@ public async Task ActionParameter_ReadOnlyCollectionModel_EmptyPrefix_DoesNotGet var state = entry.Value; Assert.NotNull(state); Assert.Equal(ModelValidationState.Valid, state.ValidationState); - Assert.Equal("SomeStreet", state.Value.RawValue); + Assert.Equal("SomeStreet", state.RawValue); + Assert.Equal("SomeStreet", state.AttemptedValue); } private class Person4 @@ -161,9 +161,8 @@ public async Task ActionParameter_SettableArrayModel_EmptyPrefix_GetsBound() Assert.Equal(1, modelState.Keys.Count); var key = Assert.Single(modelState.Keys, k => k == "Address[0].Street"); - Assert.NotNull(modelState[key].Value); - Assert.Equal("SomeStreet", modelState[key].Value.AttemptedValue); - Assert.Equal("SomeStreet", modelState[key].Value.RawValue); + Assert.Equal("SomeStreet", modelState[key].AttemptedValue); + Assert.Equal("SomeStreet", modelState[key].RawValue); Assert.Empty(modelState[key].Errors); Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); } @@ -250,9 +249,8 @@ public async Task ActionParameter_NonSettableCollectionModel_WithPrefix_GetsBoun Assert.Equal(1, modelState.Keys.Count); var key = Assert.Single(modelState.Keys, k => k == "prefix.Address[0].Street"); - Assert.NotNull(modelState[key].Value); - Assert.Equal("SomeStreet", modelState[key].Value.AttemptedValue); - Assert.Equal("SomeStreet", modelState[key].Value.RawValue); + Assert.Equal("SomeStreet", modelState[key].AttemptedValue); + Assert.Equal("SomeStreet", modelState[key].RawValue); Assert.Empty(modelState[key].Errors); Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); } @@ -300,7 +298,8 @@ public async Task ActionParameter_ReadOnlyCollectionModel_WithPrefix_DoesNotGetB var state = entry.Value; Assert.NotNull(state); Assert.Equal(ModelValidationState.Valid, state.ValidationState); - Assert.Equal("SomeStreet", state.Value.RawValue); + Assert.Equal("SomeStreet", state.AttemptedValue); + Assert.Equal("SomeStreet", state.RawValue); } [Fact] @@ -343,9 +342,8 @@ public async Task ActionParameter_SettableArrayModel_WithPrefix_GetsBound() Assert.Equal(1, modelState.Keys.Count); var key = Assert.Single(modelState.Keys, k => k == "prefix.Address[0].Street"); - Assert.NotNull(modelState[key].Value); - Assert.Equal("SomeStreet", modelState[key].Value.AttemptedValue); - Assert.Equal("SomeStreet", modelState[key].Value.RawValue); + Assert.Equal("SomeStreet", modelState[key].AttemptedValue); + Assert.Equal("SomeStreet", modelState[key].RawValue); Assert.Empty(modelState[key].Errors); Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); } diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs index 0f810fffe0..aaeac5bfd5 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/ArrayModelBinderIntegrationTest.cs @@ -44,12 +44,12 @@ public async Task ArrayModelBinder_BindsArrayOfSimpleType_WithPrefix_Success() Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -89,12 +89,12 @@ public async Task ArrayModelBinder_BindsArrayOfSimpleType_WithExplicitPrefix_Suc Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -130,12 +130,12 @@ public async Task ArrayModelBinder_BindsArrayOfSimpleType_EmptyPrefix_Success() Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -208,12 +208,12 @@ public async Task ArrayModelBinder_BindsArrayOfComplexType_WithPrefix_Success() Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0].Name").Value; - Assert.Equal("bill", entry.Value.AttemptedValue); - Assert.Equal("bill", entry.Value.RawValue); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[1].Name").Value; - Assert.Equal("lang", entry.Value.AttemptedValue); - Assert.Equal("lang", entry.Value.RawValue); + Assert.Equal("lang", entry.AttemptedValue); + Assert.Equal("lang", entry.RawValue); } [Fact] @@ -254,12 +254,12 @@ public async Task ArrayModelBinder_BindsArrayOfComplexType_WithExplicitPrefix_Su Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0].Name").Value; - Assert.Equal("bill", entry.Value.AttemptedValue); - Assert.Equal("bill", entry.Value.RawValue); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[1].Name").Value; - Assert.Equal("lang", entry.Value.AttemptedValue); - Assert.Equal("lang", entry.Value.RawValue); + Assert.Equal("lang", entry.AttemptedValue); + Assert.Equal("lang", entry.RawValue); } [Fact] @@ -296,12 +296,12 @@ public async Task ArrayModelBinder_BindsArrayOfComplexType_EmptyPrefix_Success() Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "[0].Name").Value; - Assert.Equal("bill", entry.Value.AttemptedValue); - Assert.Equal("bill", entry.Value.RawValue); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "[1].Name").Value; - Assert.Equal("lang", entry.Value.AttemptedValue); - Assert.Equal("lang", entry.Value.RawValue); + Assert.Equal("lang", entry.AttemptedValue); + Assert.Equal("lang", entry.RawValue); } [Fact] diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/BinderTypeBasedModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/BinderTypeBasedModelBinderIntegrationTest.cs index 037e5fd059..3663e6b6e9 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/BinderTypeBasedModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/BinderTypeBasedModelBinderIntegrationTest.cs @@ -190,7 +190,7 @@ public async Task BindParameter_WithData_WithPrefix_GetsBound() var key = Assert.Single(modelState.Keys); Assert.Equal("CustomParameter", key); Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); - Assert.NotNull(modelState[key].Value); // Value is set by test model binder, no need to validate it. + Assert.NotNull(modelState[key].RawValue); // Value is set by test model binder, no need to validate it. } private class Person @@ -239,7 +239,7 @@ public async Task BindProperty_WithData_EmptyPrefix_GetsBound() Assert.Equal(1, modelState.Keys.Count); var key = Assert.Single(modelState.Keys, k => k == "Parameter1.Address.Street"); Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); - Assert.NotNull(modelState[key].Value); // Value is set by test model binder, no need to validate it. + Assert.NotNull(modelState[key].RawValue); // Value is set by test model binder, no need to validate it. } [Fact] @@ -279,7 +279,7 @@ public async Task BindProperty_WithData_WithPrefix_GetsBound() Assert.Equal(1, modelState.Keys.Count); var key = Assert.Single(modelState.Keys, k => k == "CustomParameter.Address.Street"); Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); - Assert.NotNull(modelState[key].Value); // Value is set by test model binder, no need to validate it. + Assert.NotNull(modelState[key].RawValue); // Value is set by test model binder, no need to validate it. } private class AddressModelBinder : IModelBinder @@ -294,11 +294,9 @@ public Task BindModelAsync(ModelBindingContext bindingContex var address = new Address() { Street = "SomeStreet" }; bindingContext.ModelState.SetModelValue( - ModelNames.CreatePropertyModelName(bindingContext.ModelName, "Street"), - new ValueProviderResult( - address.Street, - address.Street, - CultureInfo.CurrentCulture)); + ModelNames.CreatePropertyModelName(bindingContext.ModelName, "Street"), + new string[] { address.Street }, + address.Street); var validationNode = new ModelValidationNode( bindingContext.ModelName, @@ -319,7 +317,8 @@ public Task BindModelAsync(ModelBindingContext bindingContex var model = "Success"; bindingContext.ModelState.SetModelValue( bindingContext.ModelName, - new ValueProviderResult(model, model, CultureInfo.CurrentCulture)); + new string[] { model }, + model); var modelValidationNode = new ModelValidationNode( bindingContext.ModelName, diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/BodyValidationIntegrationTests.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/BodyValidationIntegrationTests.cs index 31838694c8..ae35cfceb3 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/BodyValidationIntegrationTests.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/BodyValidationIntegrationTests.cs @@ -102,7 +102,7 @@ public async Task FromBodyOnActionParameter_EmptyBody_BindsToNullValue() Assert.True(modelState.IsValid); var entry = Assert.Single(modelState); Assert.Empty(entry.Key); - Assert.Null(entry.Value.Value.RawValue); + Assert.Null(entry.Value.RawValue); } private class Person4 @@ -112,7 +112,7 @@ private class Person4 public int Address { get; set; } } - [Fact] + [Fact(Skip = "#2722 validation error from formatter is recorded with the wrong key.")] public async Task FromBodyAndRequiredOnValueTypeProperty_EmptyBody_JsonFormatterAddsModelStateError() { // Arrange @@ -146,7 +146,9 @@ public async Task FromBodyAndRequiredOnValueTypeProperty_EmptyBody_JsonFormatter Assert.False(modelState.IsValid); var entry = Assert.Single(modelState); - Assert.Equal(string.Empty, entry.Key); + Assert.Equal("CustomParameter.Address", entry.Key); + Assert.Null(entry.Value.AttemptedValue); + Assert.Same(boundPerson, entry.Value.RawValue); var error = Assert.Single(entry.Value.Errors); Assert.NotNull(error.Exception); diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/ByteArrayModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/ByteArrayModelBinderIntegrationTest.cs index 79ca9e9651..b1f0e53be5 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/ByteArrayModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/ByteArrayModelBinderIntegrationTest.cs @@ -65,8 +65,8 @@ public async Task BindProperty_WithData_GetsBound(bool fallBackScenario) Assert.Equal(queryStringKey, entry.Key); Assert.Empty(entry.Value.Errors); Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState); - Assert.Equal(value, entry.Value.Value.AttemptedValue); - Assert.Equal(value, entry.Value.Value.RawValue); + Assert.Equal(value, entry.Value.AttemptedValue); + Assert.Equal(value, entry.Value.RawValue); } [Fact] @@ -146,8 +146,8 @@ public async Task BindParameter_WithData_GetsBound() var entry = Assert.Single(modelState); Assert.Equal("CustomParameter", entry.Key); Assert.Empty(entry.Value.Errors); - Assert.Equal(value, entry.Value.Value.AttemptedValue); - Assert.Equal(value, entry.Value.Value.RawValue); + Assert.Equal(value, entry.Value.AttemptedValue); + Assert.Equal(value, entry.Value.RawValue); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs index 1037f03c09..2ba4de5549 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs @@ -51,12 +51,12 @@ public async Task CollectionModelBinder_BindsListOfSimpleType_WithPrefix_Success Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -96,12 +96,12 @@ public async Task CollectionModelBinder_BindsListOfSimpleType_WithExplicitPrefix Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -137,12 +137,12 @@ public async Task CollectionModelBinder_BindsCollectionOfSimpleType_EmptyPrefix_ Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -215,12 +215,12 @@ public async Task CollectionModelBinder_BindsListOfComplexType_WithPrefix_Succes Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0].Id").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[1].Id").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -261,12 +261,12 @@ public async Task CollectionModelBinder_BindsListOfComplexType_WithExplicitPrefi Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0].Id").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[1].Id").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -303,12 +303,12 @@ public async Task CollectionModelBinder_BindsCollectionOfComplexType_EmptyPrefix Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "[0].Id").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "[1].Id").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -386,21 +386,21 @@ public async Task CollectionModelBinder_BindsListOfComplexType_WithRequiredPrope Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0].Id").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[1].Id").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0].Name").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); var error = Assert.Single(entry.Errors); Assert.Equal("A value for the 'Name' property was not provided.", error.ErrorMessage); entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[1].Name").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); error = Assert.Single(entry.Errors); Assert.Equal("A value for the 'Name' property was not provided.", error.ErrorMessage); @@ -446,19 +446,19 @@ public async Task CollectionModelBinder_BindsListOfComplexType_WithRequiredPrope Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0].Id").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[1].Id").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0].Name").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[1].Name").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); } @@ -498,19 +498,19 @@ public async Task CollectionModelBinder_BindsCollectionOfComplexType_WithRequire Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "[0].Id").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "[1].Id").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "[0].Name").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); entry = Assert.Single(modelState, kvp => kvp.Key == "[1].Name").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); } @@ -595,12 +595,12 @@ public async Task CollectionModelBinder_UsesCustomIndexes() Assert.Equal(0, modelState.ErrorCount); Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "Addresses[Key1].Street").Value; - Assert.Equal("Street1", entry.Value.AttemptedValue); - Assert.Equal("Street1", entry.Value.RawValue); + Assert.Equal("Street1", entry.AttemptedValue); + Assert.Equal("Street1", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "Addresses[Key2].Street").Value; - Assert.Equal("Street2", entry.Value.AttemptedValue); - Assert.Equal("Street2", entry.Value.RawValue); + Assert.Equal("Street2", entry.AttemptedValue); + Assert.Equal("Street2", entry.RawValue); } private class Person5 diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs index e60eb1aacd..13fcc86b20 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/DictionaryModelBinderIntegrationTest.cs @@ -45,12 +45,12 @@ public async Task DictionaryModelBinder_BindsDictionaryOfSimpleType_WithPrefixAn 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); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0].Value").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Fact] @@ -87,8 +87,8 @@ public async Task DictionaryModelBinder_BindsDictionaryOfSimpleType_WithPrefixAn 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); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Theory] diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/FormFileModelBindingIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/FormFileModelBindingIntegrationTest.cs index 89c21c0ed2..378240d674 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/FormFileModelBindingIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/FormFileModelBindingIntegrationTest.cs @@ -71,7 +71,7 @@ public async Task BindProperty_WithData_WithEmptyPrefix_GetsBound() Assert.Equal(2, modelState.Count); Assert.Single(modelState.Keys, k => k == "Address.Zip"); var key = Assert.Single(modelState.Keys, k => k == "Address.File"); - Assert.NotNull(modelState[key].Value); + Assert.Null(modelState[key].RawValue); Assert.Empty(modelState[key].Errors); Assert.Equal(ModelValidationState.Skipped, modelState[key].ValidationState); } @@ -122,8 +122,8 @@ public async Task BindParameter_WithData_GetsBound() Assert.Equal("CustomParameter", entry.Key); Assert.Empty(entry.Value.Errors); Assert.Equal(ModelValidationState.Skipped, entry.Value.ValidationState); - Assert.Null(entry.Value.Value.AttemptedValue); - Assert.Equal(file, entry.Value.Value.RawValue); + Assert.Null(entry.Value.AttemptedValue); + Assert.Null(entry.Value.RawValue); } [Fact] diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs index 3395ce7fa6..3ef6aa75c9 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs @@ -276,12 +276,12 @@ public async Task GenericModelBinder_BindsArrayOfDictionary_WithPrefix_Success() Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter[0][0].Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter[0][0].Value").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } // This is part of a random sampling of scenarios where a GenericModelBinder is used @@ -322,12 +322,12 @@ public async Task GenericModelBinder_BindsArrayOfDictionary_EmptyPrefix_Success( Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "[0][0].Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "[0][0].Value").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } // This is part of a random sampling of scenarios where a GenericModelBinder is used @@ -403,12 +403,12 @@ public async Task GenericModelBinder_BindsCollectionOfKeyValuePair_WithPrefix_Su Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter[0].Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter[0].Value").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } // This is part of a random sampling of scenarios where a GenericModelBinder is used @@ -448,12 +448,12 @@ public async Task GenericModelBinder_BindsCollectionOfKeyValuePair_EmptyPrefix_S Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "[0].Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "[0].Value").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } // This is part of a random sampling of scenarios where a GenericModelBinder is used @@ -530,16 +530,16 @@ public async Task GenericModelBinder_BindsDictionaryOfList_WithPrefix_Success() Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter[0].Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter[0].Value[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter[0].Value[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } // This is part of a random sampling of scenarios where a GenericModelBinder is used @@ -579,16 +579,16 @@ public async Task GenericModelBinder_BindsDictionaryOfList_EmptyPrefix_Success() Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "[0].Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "[0].Value[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "[0].Value[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } // This is part of a random sampling of scenarios where a GenericModelBinder is used diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/HeaderModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/HeaderModelBinderIntegrationTest.cs index 04dc1e36c2..2e30b896a9 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/HeaderModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/HeaderModelBinderIntegrationTest.cs @@ -109,8 +109,8 @@ public async Task BindPropertyFromHeader_WithPrefix_GetsBound() Assert.Equal("prefix.Address.Header", entry.Key); Assert.Empty(entry.Value.Errors); Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState); - Assert.Equal("someValue", entry.Value.Value.AttemptedValue); - Assert.Equal("someValue", entry.Value.Value.RawValue); + Assert.Equal("someValue", entry.Value.AttemptedValue); + Assert.Equal(new string[] { "someValue" }, entry.Value.RawValue); } // The scenario is interesting as we to bind the top level model we fallback to empty prefix, @@ -152,8 +152,8 @@ public async Task BindPropertyFromHeader_WithData_WithEmptyPrefix_GetsBound() Assert.Equal("Address.Header", entry.Key); Assert.Empty(entry.Value.Errors); Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState); - Assert.Equal("someValue", entry.Value.Value.AttemptedValue); - Assert.Equal("someValue", entry.Value.Value.RawValue); + Assert.Equal("someValue", entry.Value.AttemptedValue); + Assert.Equal(new string[] { "someValue" }, entry.Value.RawValue); } [Theory] @@ -162,9 +162,18 @@ public async Task BindPropertyFromHeader_WithData_WithEmptyPrefix_GetsBound() public async Task BindParameterFromHeader_WithData_WithPrefix_ModelGetsBound(Type modelType, string value) { // Arrange - var expectedValue = modelType == typeof(string) ? - (object)value : - (object)value.Split(',').Select(v => v.Trim()).ToArray(); + object expectedValue; + object expectedRawValue; + if (modelType == typeof(string)) + { + expectedValue = value; + expectedRawValue = new string[] { value }; + } + else + { + expectedValue = value.Split(',').Select(v => v.Trim()).ToArray(); + expectedRawValue = expectedValue; + } var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor @@ -204,8 +213,8 @@ public async Task BindParameterFromHeader_WithData_WithPrefix_ModelGetsBound(Typ Assert.Equal("CustomParameter", entry.Key); Assert.Empty(entry.Value.Errors); Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState); - Assert.Equal(value, entry.Value.Value.AttemptedValue); - Assert.Equal(expectedValue, entry.Value.Value.RawValue); + Assert.Equal(value, entry.Value.AttemptedValue); + Assert.Equal(expectedRawValue, entry.Value.RawValue); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs index 58071797f7..b2fc870312 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs @@ -45,12 +45,12 @@ public async Task KeyValuePairModelBinder_BindsKeyValuePairOfSimpleType_WithPref Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Value").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Fact] @@ -90,12 +90,12 @@ public async Task KeyValuePairModelBinder_BindsKeyValuePairOfSimpleType_WithExpl Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix.Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "prefix.Value").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Fact] @@ -131,12 +131,12 @@ public async Task KeyValuePairModelBinder_BindsKeyValuePairOfSimpleType_EmptyPre Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "Value").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Fact] @@ -210,12 +210,12 @@ public async Task KeyValuePairModelBinder_BindsKeyValuePairOfComplexType_WithPre Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Value.Id").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal(model.Value.Id.ToString(), entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Fact] @@ -256,12 +256,12 @@ public async Task KeyValuePairModelBinder_BindsKeyValuePairOfComplexType_WithExp Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix.Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "prefix.Value.Id").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Fact] @@ -298,12 +298,12 @@ public async Task KeyValuePairModelBinder_BindsKeyValuePairOfComplexType_EmptyPr Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, kvp => kvp.Key == "Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, kvp => kvp.Key == "Value.Id").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Fact] diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/MutableObjectModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/MutableObjectModelBinderIntegrationTest.cs index d3f709ec4c..7dd3d4be18 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/MutableObjectModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/MutableObjectModelBinderIntegrationTest.cs @@ -82,12 +82,12 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_W 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); 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); + Assert.Null(entry.AttemptedValue); // ModelState entries for body don't include original text. + Assert.Same(model.Customer.Address, entry.RawValue); } [Fact] @@ -128,12 +128,12 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_W 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); 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); + Assert.Null(entry.AttemptedValue); // ModelState entries for body don't include original text. + Assert.Same(model.Customer.Address, entry.RawValue); } [Fact] @@ -173,11 +173,11 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_W Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Address").Value; - Assert.Null(entry.Value.AttemptedValue); - Assert.Null(entry.Value.RawValue); + Assert.Null(entry.AttemptedValue); + Assert.Null(entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; - Assert.Equal("bill", entry.Value.AttemptedValue); - Assert.Equal("bill", entry.Value.RawValue); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there is @@ -218,8 +218,8 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_W 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); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there is @@ -310,8 +310,8 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithServicesModelBind 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); } [Fact] @@ -350,8 +350,8 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithServicesModelBind 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there is @@ -392,8 +392,8 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithServicesModelBind 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); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there is @@ -484,12 +484,12 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithByteArrayModelBin 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Token").Value; - Assert.Equal(ByteArrayEncoded, entry.Value.AttemptedValue); - Assert.Equal(ByteArrayEncoded, entry.Value.RawValue); + Assert.Equal(ByteArrayEncoded, entry.AttemptedValue); + Assert.Equal(ByteArrayEncoded, entry.RawValue); } [Fact] @@ -528,12 +528,12 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithByteArrayModelBin 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "Customer.Token").Value; - Assert.Equal(ByteArrayEncoded, entry.Value.AttemptedValue); - Assert.Equal(ByteArrayEncoded, entry.Value.RawValue); + Assert.Equal(ByteArrayEncoded, entry.AttemptedValue); + Assert.Equal(ByteArrayEncoded, entry.RawValue); } [Fact] @@ -572,8 +572,8 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithByteArrayModelBin 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); } private class Order4 @@ -627,12 +627,12 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBind 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.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); + Assert.Null(entry.AttemptedValue); // FormFile entries for body don't include original text. + Assert.Null(entry.RawValue); } [Fact] @@ -672,12 +672,12 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBind 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.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); + Assert.Null(entry.AttemptedValue); // FormFile entries don't include the model. + Assert.Null(entry.RawValue); } [Fact] @@ -718,13 +718,12 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBind Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Documents").Value; - Assert.Null(entry.Value.AttemptedValue); - var documents = Assert.IsAssignableFrom>(entry.Value.RawValue); - Assert.Empty(documents); + Assert.Null(entry.AttemptedValue); // FormFile entries don't include the model. + Assert.Null(entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; - Assert.Equal("bill", entry.Value.AttemptedValue); - Assert.Equal("bill", entry.Value.RawValue); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there are @@ -765,8 +764,8 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBind 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); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } // We don't provide enough data in this test for the 'Person' model to be created. So even though there is @@ -849,16 +848,16 @@ public async Task MutableObjectModelBinder_BindsArrayProperty_WithPrefix_Success 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductIds[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductIds[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -897,16 +896,16 @@ public async Task MutableObjectModelBinder_BindsArrayProperty_EmptyPrefix_Succes 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -944,8 +943,8 @@ public async Task MutableObjectModelBinder_BindsArrayProperty_NoCollectionData() 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); } [Fact] @@ -1026,16 +1025,16 @@ public async Task MutableObjectModelBinder_BindsListProperty_WithPrefix_Success( 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductIds[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductIds[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -1074,16 +1073,16 @@ public async Task MutableObjectModelBinder_BindsListProperty_EmptyPrefix_Success 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[0]").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[1]").Value; - Assert.Equal("11", entry.Value.AttemptedValue); - Assert.Equal("11", entry.Value.RawValue); + Assert.Equal("11", entry.AttemptedValue); + Assert.Equal("11", entry.RawValue); } [Fact] @@ -1121,8 +1120,8 @@ public async Task MutableObjectModelBinder_BindsListProperty_NoCollectionData() 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); } [Fact] @@ -1203,16 +1202,16 @@ public async Task MutableObjectModelBinder_BindsDictionaryProperty_WithPrefix_Su 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.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); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.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); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Fact] @@ -1251,16 +1250,16 @@ public async Task MutableObjectModelBinder_BindsDictionaryProperty_EmptyPrefix_S 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[0].Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductIds[0].Value").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Fact] @@ -1298,8 +1297,8 @@ public async Task MutableObjectModelBinder_BindsDictionaryProperty_NoCollectionD 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); } [Fact] @@ -1380,16 +1379,16 @@ public async Task MutableObjectModelBinder_BindsKeyValuePairProperty_WithPrefix_ 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductId.Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.ProductId.Value").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Fact] @@ -1428,16 +1427,16 @@ public async Task MutableObjectModelBinder_BindsKeyValuePairProperty_EmptyPrefix 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductId.Key").Value; - Assert.Equal("key0", entry.Value.AttemptedValue); - Assert.Equal("key0", entry.Value.RawValue); + Assert.Equal("key0", entry.AttemptedValue); + Assert.Equal("key0", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "ProductId.Value").Value; - Assert.Equal("10", entry.Value.AttemptedValue); - Assert.Equal("10", entry.Value.RawValue); + Assert.Equal("10", entry.AttemptedValue); + Assert.Equal("10", entry.RawValue); } [Fact] @@ -1475,8 +1474,8 @@ public async Task MutableObjectModelBinder_BindsKeyValuePairProperty_NoCollectio 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); } [Fact] @@ -1568,8 +1567,8 @@ public async Task MutableObjectModelBinder_BindsNestedPOCO_WithAllGreedyBoundPro Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Customer.Address").Value; - Assert.Null(entry.Value.AttemptedValue); - Assert.Same(model.Customer.Address, entry.Value.RawValue); + Assert.Null(entry.AttemptedValue); + Assert.Same(model.Customer.Address, entry.RawValue); } private class Order10 @@ -1616,7 +1615,8 @@ public async Task MutableObjectModelBinder_WithRequiredComplexProperty_NoData_Ge Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Customer").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); var error = Assert.Single(modelState["Customer"].Errors); Assert.Equal("A value for the 'Customer' property was not provided.", error.ErrorMessage); } @@ -1670,12 +1670,12 @@ public async Task MutableObjectModelBinder_WithNestedRequiredProperty_WithPartia Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Id").Value; - Assert.NotNull(entry.Value); - Assert.Equal("123", entry.Value.RawValue); - Assert.Equal("123", entry.Value.AttemptedValue); + Assert.Equal("123", entry.RawValue); + Assert.Equal("123", entry.AttemptedValue); entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); var error = Assert.Single(modelState["parameter.Customer.Name"].Errors); Assert.Equal("A value for the 'Name' property was not provided.", error.ErrorMessage); } @@ -1716,12 +1716,12 @@ public async Task MutableObjectModelBinder_WithNestedRequiredProperty_WithData_E Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Customer.Id").Value; - Assert.NotNull(entry.Value); - Assert.Equal("123", entry.Value.RawValue); - Assert.Equal("123", entry.Value.AttemptedValue); + Assert.Equal("123", entry.RawValue); + Assert.Equal("123", entry.AttemptedValue); entry = Assert.Single(modelState, e => e.Key == "Customer.Name").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); var error = Assert.Single(modelState["Customer.Name"].Errors); Assert.Equal("A value for the 'Name' property was not provided.", error.ErrorMessage); } @@ -1766,12 +1766,12 @@ public async Task MutableObjectModelBinder_WithNestedRequiredProperty_WithData_C Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "customParameter.Customer.Id").Value; - Assert.NotNull(entry.Value); - Assert.Equal("123", entry.Value.RawValue); - Assert.Equal("123", entry.Value.AttemptedValue); + Assert.Equal("123", entry.RawValue); + Assert.Equal("123", entry.AttemptedValue); entry = Assert.Single(modelState, e => e.Key == "customParameter.Customer.Name").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); var error = Assert.Single(modelState["customParameter.Customer.Name"].Errors); Assert.Equal("A value for the 'Name' property was not provided.", error.ErrorMessage); } @@ -1816,7 +1816,8 @@ public async Task MutableObjectModelBinder_WithRequiredProperty_NoData_GetsError Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "ProductName").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); var error = Assert.Single(modelState["ProductName"].Errors); Assert.Equal("A value for the 'ProductName' property was not provided.", error.ErrorMessage); } @@ -1859,7 +1860,8 @@ public async Task MutableObjectModelBinder_WithRequiredProperty_NoData_CustomPre Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "customParameter.ProductName").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); var error = Assert.Single(modelState["customParameter.ProductName"].Errors); Assert.Equal("A value for the 'ProductName' property was not provided.", error.ErrorMessage); } @@ -1898,9 +1900,8 @@ public async Task MutableObjectModelBinder_WithRequiredProperty_WithData_EmptyPr Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "ProductName").Value; - Assert.NotNull(entry.Value); - Assert.Equal("abc", entry.Value.RawValue); - Assert.Equal("abc", entry.Value.AttemptedValue); + Assert.Equal("abc", entry.RawValue); + Assert.Equal("abc", entry.AttemptedValue); } private class Order13 @@ -1943,7 +1944,8 @@ public async Task MutableObjectModelBinder_WithRequiredCollectionProperty_NoData Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "OrderIds").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); var error = Assert.Single(modelState["OrderIds"].Errors); Assert.Equal("A value for the 'OrderIds' property was not provided.", error.ErrorMessage); } @@ -1986,7 +1988,8 @@ public async Task MutableObjectModelBinder_WithRequiredCollectionProperty_NoData Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "customParameter.OrderIds").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); var error = Assert.Single(modelState["customParameter.OrderIds"].Errors); Assert.Equal("A value for the 'OrderIds' property was not provided.", error.ErrorMessage); } @@ -2025,9 +2028,8 @@ public async Task MutableObjectModelBinder_WithRequiredCollectionProperty_WithDa Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "OrderIds[0]").Value; - Assert.NotNull(entry.Value); - Assert.Equal("123", entry.Value.RawValue); - Assert.Equal("123", entry.Value.AttemptedValue); + Assert.Equal("123", entry.RawValue); + Assert.Equal("123", entry.AttemptedValue); } private class Order14 @@ -2072,8 +2074,8 @@ public async Task MutableObjectModelBinder_BindsPOCO_TypeConvertedPropertyNonCon Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.ProductId").Value; - Assert.Equal(string.Empty, entry.Value.AttemptedValue); - Assert.Equal(string.Empty, entry.Value.RawValue); + Assert.Equal(string.Empty, entry.AttemptedValue); + Assert.Equal(string.Empty, entry.RawValue); var error = Assert.Single(entry.Errors); Assert.Equal("The value '' is invalid.", error.ErrorMessage); diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/TypeConverterModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs similarity index 83% rename from test/Microsoft.AspNet.Mvc.IntegrationTests/TypeConverterModelBinderIntegrationTest.cs rename to test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs index 3b87dbbd12..80618136dc 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/TypeConverterModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests { - public class TypeConverterModelBinderIntegrationTest + public class SimpleTypeModelBinderIntegrationTest { [Fact] public async Task BindProperty_WithData_WithPrefix_GetsBound() @@ -58,10 +58,8 @@ public async Task BindProperty_WithData_WithPrefix_GetsBound() Assert.Equal(1, modelState.Keys.Count); var key = Assert.Single(modelState.Keys, k => k == "CustomParameter.Address.Zip"); - Assert.NotNull(modelState[key].Value); - Assert.Equal("1", modelState[key].Value.AttemptedValue); - Assert.Equal("1", modelState[key].Value.RawValue); - Assert.NotNull(modelState[key].Value); + Assert.Equal("1", modelState[key].AttemptedValue); + Assert.Equal("1", modelState[key].RawValue); Assert.Empty(modelState[key].Errors); Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); } @@ -105,10 +103,8 @@ public async Task BindProperty_WithData_WithEmptyPrefix_GetsBound() Assert.Equal(1, modelState.Keys.Count); var key = Assert.Single(modelState.Keys, k => k == "Address.Zip"); - Assert.NotNull(modelState[key].Value); - Assert.Equal("1", modelState[key].Value.AttemptedValue); - Assert.Equal("1", modelState[key].Value.RawValue); - Assert.NotNull(modelState[key].Value); + Assert.Equal("1", modelState[key].AttemptedValue); + Assert.Equal("1", modelState[key].RawValue); Assert.Empty(modelState[key].Errors); Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); } @@ -152,13 +148,56 @@ public async Task BindParameter_WithData_GetsBound() Assert.Equal(1, modelState.Keys.Count); var key = Assert.Single(modelState.Keys); Assert.Equal("Parameter1", key); - Assert.NotNull(modelState[key].Value); - Assert.Equal("someValue", modelState[key].Value.AttemptedValue); - Assert.Equal("someValue", modelState[key].Value.RawValue); + Assert.Equal("someValue", modelState[key].AttemptedValue); + Assert.Equal("someValue", modelState[key].RawValue); Assert.Empty(modelState[key].Errors); Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); } + [Fact] + public async Task BindParameter_WithMultipleValues_GetsBoundToFirstValue() + { + // Arrange + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); + var parameter = new ParameterDescriptor() + { + Name = "Parameter1", + BindingInfo = new BindingInfo(), + + ParameterType = typeof(string) + }; + + var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => + { + request.QueryString = new QueryString("?Parameter1=someValue&Parameter1=otherValue"); + }); + + var modelState = new ModelStateDictionary(); + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); + + // Assert + + // ModelBindingResult + Assert.NotNull(modelBindingResult); + Assert.True(modelBindingResult.IsModelSet); + + // Model + var model = Assert.IsType(modelBindingResult.Model); + Assert.Equal("someValue", model); + + // ModelState + Assert.True(modelState.IsValid); + + Assert.Equal(1, modelState.Keys.Count); + var key = Assert.Single(modelState.Keys); + Assert.Equal("Parameter1", key); + Assert.Equal("someValue,otherValue", modelState[key].AttemptedValue); + Assert.Equal(new string[] { "someValue", "otherValue" }, modelState[key].RawValue); + Assert.Empty(modelState[key].Errors); + Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); + } [Fact] public async Task BindParameter_NonConvertableValue_GetsError() @@ -201,9 +240,8 @@ public async Task BindParameter_NonConvertableValue_GetsError() Assert.Equal("Parameter1", key); var entry = modelState[key]; - Assert.NotNull(entry.Value); - Assert.Equal("abcd", entry.Value.AttemptedValue); - Assert.Equal("abcd", entry.Value.RawValue); + Assert.Equal("abcd", entry.RawValue); + Assert.Equal("abcd", entry.AttemptedValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); var error = Assert.Single(entry.Errors); @@ -247,8 +285,8 @@ public async Task BindParameter_WithEmptyData_DoesNotBind(Type parameterType) Assert.False(modelState.IsValid); var key = Assert.Single(modelState.Keys); Assert.Equal("Parameter1", key); - Assert.Equal(string.Empty, modelState[key].Value.AttemptedValue); - Assert.Equal(string.Empty, modelState[key].Value.RawValue); + Assert.Equal(string.Empty, modelState[key].AttemptedValue); + Assert.Equal(string.Empty, modelState[key].RawValue); var error = Assert.Single(modelState[key].Errors); Assert.Equal(error.ErrorMessage, "The value '' is invalid.", StringComparer.Ordinal); Assert.Null(error.Exception); @@ -292,8 +330,8 @@ public async Task BindParameter_WithEmptyData_BindsMutableAndNullableObjects(Typ Assert.True(modelState.IsValid); var key = Assert.Single(modelState.Keys); Assert.Equal("Parameter1", key); - Assert.Equal(string.Empty, modelState[key].Value.AttemptedValue); - Assert.Equal(string.Empty, modelState[key].Value.RawValue); + Assert.Equal(string.Empty, modelState[key].AttemptedValue); + Assert.Equal(new string[] { string.Empty }, modelState[key].RawValue); Assert.Empty(modelState[key].Errors); } @@ -393,10 +431,8 @@ public async Task BindParameter_FromFormData_BindsCorrectly(IDictionary e.Key == "parameter.CustomerName").Value; - Assert.Equal("bill", entry.Value.AttemptedValue); - Assert.Equal("bill", entry.Value.RawValue); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); Assert.Empty(entry.Errors); } @@ -91,7 +91,8 @@ public async Task Validation_RequiredAttribute_OnSimpleTypeProperty_NoData() Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "CustomerName").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); var error = Assert.Single(entry.Errors); @@ -143,8 +144,8 @@ public async Task Validation_RequiredAttribute_OnPOCOProperty_WithData() 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); Assert.Empty(entry.Errors); } @@ -181,7 +182,8 @@ public async Task Validation_RequiredAttribute_OnPOCOProperty_NoData() Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Customer").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); var error = Assert.Single(entry.Errors); @@ -235,8 +237,8 @@ public async Task Validation_RequiredAttribute_OnNestedSimpleTypeProperty_WithDa 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); Assert.Empty(entry.Errors); } @@ -276,7 +278,8 @@ public async Task Validation_RequiredAttribute_OnNestedSimpleTypeProperty_NoData Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); var error = Assert.Single(entry.Errors); @@ -328,8 +331,8 @@ public async Task Validation_RequiredAttribute_OnCollectionProperty_WithData() Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Items[0].ItemId").Value; - Assert.Equal("17", entry.Value.AttemptedValue); - Assert.Equal("17", entry.Value.RawValue); + Assert.Equal("17", entry.AttemptedValue); + Assert.Equal("17", entry.RawValue); Assert.Empty(entry.Errors); } @@ -367,7 +370,8 @@ public async Task Validation_RequiredAttribute_OnCollectionProperty_NoData() Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "Items").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); var error = Assert.Single(entry.Errors); @@ -415,8 +419,8 @@ public async Task Validation_RequiredAttribute_OnPOCOPropertyOfBoundElement_With Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter[0].ProductId").Value; - Assert.Equal("17", entry.Value.AttemptedValue); - Assert.Equal("17", entry.Value.RawValue); + Assert.Equal("17", entry.AttemptedValue); + Assert.Equal("17", entry.RawValue); Assert.Empty(entry.Errors); } @@ -456,7 +460,8 @@ public async Task Validation_RequiredAttribute_OnPOCOPropertyOfBoundElement_NoDa Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter[0].ProductId").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); var error = Assert.Single(entry.Errors); @@ -502,8 +507,8 @@ public async Task Validation_StringLengthAttribute_OnPropertyOfPOCO_Valid() 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); Assert.Empty(entry.Errors); } @@ -540,8 +545,8 @@ public async Task Validation_StringLengthAttribute_OnPropertyOfPOCO_Invalid() Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Name").Value; - Assert.Equal("billybob", entry.Value.AttemptedValue); - Assert.Equal("billybob", entry.Value.RawValue); + Assert.Equal("billybob", entry.AttemptedValue); + Assert.Equal("billybob", entry.RawValue); var error = Assert.Single(entry.Errors); Assert.Equal("Too Long.", error.ErrorMessage); @@ -592,8 +597,8 @@ public async Task Validation_StringLengthAttribute_OnPropertyOfNestedPOCO_Valid( 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); Assert.Empty(entry.Errors); } @@ -630,8 +635,8 @@ public async Task Validation_StringLengthAttribute_OnPropertyOfNestedPOCO_Invali Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; - Assert.Equal("billybob", entry.Value.AttemptedValue); - Assert.Equal("billybob", entry.Value.RawValue); + Assert.Equal("billybob", entry.AttemptedValue); + Assert.Equal("billybob", entry.RawValue); var error = Assert.Single(entry.Errors); Assert.Equal("Too Long.", error.ErrorMessage); @@ -730,8 +735,8 @@ public async Task Validation_CustomAttribute_OnPOCOProperty_Valid() 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); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); Assert.Empty(entry.Errors); } @@ -768,11 +773,12 @@ public async Task Validation_CustomAttribute_OnPOCOProperty_Invalid() Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; - Assert.Equal("billybob", entry.Value.AttemptedValue); - Assert.Equal("billybob", entry.Value.RawValue); + Assert.Equal("billybob", entry.AttemptedValue); + Assert.Equal("billybob", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.Customer").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); var error = Assert.Single(entry.Errors); Assert.Equal("Invalid Person.", error.ErrorMessage); @@ -838,8 +844,8 @@ public async Task Validation_CustomAttribute_OnCollectionElement_Valid() Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Products[0].Name").Value; - Assert.Equal("bill", entry.Value.AttemptedValue); - Assert.Equal("bill", entry.Value.RawValue); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); Assert.Empty(entry.Errors); } @@ -876,11 +882,12 @@ public async Task Validation_CustomAttribute_OnCollectionElement_Invalid() Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter.Products[0].Name").Value; - Assert.Equal("billybob", entry.Value.AttemptedValue); - Assert.Equal("billybob", entry.Value.RawValue); + Assert.Equal("billybob", entry.AttemptedValue); + Assert.Equal("billybob", entry.RawValue); entry = Assert.Single(modelState, e => e.Key == "parameter.Products").Value; - Assert.Null(entry.Value); + Assert.Null(entry.RawValue); + Assert.Null(entry.AttemptedValue); Assert.Equal(ModelValidationState.Invalid, entry.ValidationState); var error = Assert.Single(entry.Errors); @@ -927,8 +934,8 @@ public async Task Validation_StringLengthAttribute_OnProperyOfCollectionElement_ Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter[0].Name").Value; - Assert.Equal("bill", entry.Value.AttemptedValue); - Assert.Equal("bill", entry.Value.RawValue); + Assert.Equal("bill", entry.AttemptedValue); + Assert.Equal("bill", entry.RawValue); Assert.Empty(entry.Errors); } @@ -965,8 +972,8 @@ public async Task Validation_StringLengthAttribute_OnProperyOfCollectionElement_ Assert.False(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "parameter[0].Name").Value; - Assert.Equal("billybob", entry.Value.AttemptedValue); - Assert.Equal("billybob", entry.Value.RawValue); + Assert.Equal("billybob", entry.AttemptedValue); + Assert.Equal("billybob", entry.RawValue); var error = Assert.Single(entry.Errors); Assert.Equal("Too Long.", error.ErrorMessage); @@ -1070,23 +1077,23 @@ public async Task TypeBasedExclusion_ForBodyAndNonBodyBoundModels() Assert.True(modelState.IsValid); var entry = Assert.Single(modelState, e => e.Key == "HomeAddress.Country.Name").Value; - Assert.Equal("US", entry.Value.AttemptedValue); - Assert.Equal("US", entry.Value.RawValue); + Assert.Equal("US", entry.AttemptedValue); + Assert.Equal("US", entry.RawValue); Assert.Equal(ModelValidationState.Skipped, entry.ValidationState); entry = Assert.Single(modelState, e => e.Key == "ShippingAddresses[0].Zip").Value; - Assert.Equal("45", entry.Value.AttemptedValue); - Assert.Equal("45", entry.Value.RawValue); + Assert.Equal("45", entry.AttemptedValue); + Assert.Equal("45", entry.RawValue); Assert.Equal(ModelValidationState.Skipped, entry.ValidationState); entry = Assert.Single(modelState, e => e.Key == "HomeAddress.Zip").Value; - Assert.Equal("46", entry.Value.AttemptedValue); - Assert.Equal("46", entry.Value.RawValue); + Assert.Equal("46", entry.AttemptedValue); + Assert.Equal("46", entry.RawValue); Assert.Equal(ModelValidationState.Skipped, entry.ValidationState); entry = Assert.Single(modelState, e => e.Key == "OfficeAddress").Value; - Assert.Null(entry.Value.AttemptedValue); - var address = Assert.IsType
(entry.Value.RawValue); + Assert.Null(entry.AttemptedValue); + var address = Assert.IsType
(entry.RawValue); Assert.Equal(47, address.Zip); // Address itself is not excluded from validation. diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs index 1f55e2b444..eeefe46b69 100644 --- a/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs +++ b/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs @@ -39,13 +39,12 @@ public void Setup_SetsUpModelBinders() // Assert var i = 0; - Assert.Equal(12, options.ModelBinders.Count); + Assert.Equal(11, options.ModelBinders.Count); Assert.IsType(typeof(BinderTypeBasedModelBinder), options.ModelBinders[i++]); Assert.IsType(typeof(ServicesModelBinder), options.ModelBinders[i++]); Assert.IsType(typeof(BodyModelBinder), options.ModelBinders[i++]); Assert.IsType(typeof(HeaderModelBinder), options.ModelBinders[i++]); - Assert.IsType(typeof(TypeConverterModelBinder), options.ModelBinders[i++]); - Assert.IsType(typeof(TypeMatchModelBinder), options.ModelBinders[i++]); + Assert.IsType(typeof(SimpleTypeModelBinder), options.ModelBinders[i++]); Assert.IsType(typeof(CancellationTokenModelBinder), options.ModelBinders[i++]); Assert.IsType(typeof(ByteArrayModelBinder), options.ModelBinders[i++]); Assert.IsType(typeof(FormFileModelBinder), options.ModelBinders[i++]); diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/DefaultHtmlGeneratorTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/DefaultHtmlGeneratorTest.cs index e2752bdbe2..16cdf52fdf 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/DefaultHtmlGeneratorTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/DefaultHtmlGeneratorTest.cs @@ -216,17 +216,13 @@ public void GetCurrentValues_AllowMultipleWithNonEnumerableInViewData_Throws(obj } // rawValue, allowMultiple -> expected current values - public static TheoryData> GetCurrentValues_StringAndCollectionData + public static TheoryData> GetCurrentValues_CollectionData { get { - return new TheoryData> + return new TheoryData> { - // ModelStateDictionary converts single values to arrays and visa-versa. - { string.Empty, false, new [] { string.Empty } }, - { string.Empty, true, new [] { string.Empty } }, - { "some string", false, new [] { "some string" } }, - { "some string", true, new [] { "some string" } }, + // ModelStateDictionary converts arrays to single values if needed. { new [] { "some string" }, false, new [] { "some string" } }, { new [] { "some string" }, true, new [] { "some string" } }, { new [] { "some string", "some other string" }, false, new [] { "some string" } }, @@ -261,9 +257,9 @@ public static TheoryData> GetCurrentVa } [Theory] - [MemberData(nameof(GetCurrentValues_StringAndCollectionData))] + [MemberData(nameof(GetCurrentValues_CollectionData))] public void GetCurrentValues_WithModelStateEntryAndViewData_ReturnsModelStateEntry( - object rawValue, + string[] rawValue, bool allowMultiple, IReadOnlyCollection expected) { @@ -274,10 +270,7 @@ public void GetCurrentValues_WithModelStateEntryAndViewData_ReturnsModelStateEnt var viewContext = GetViewContext(model, metadataProvider); viewContext.ViewData[nameof(Model.Name)] = "ignored ViewData value"; - - - var valueProviderResult = new ValueProviderResult(rawValue); - viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult); + viewContext.ModelState.SetModelValue(nameof(Model.Name), rawValue, attemptedValue: null); // Act var result = htmlGenerator.GetCurrentValues( @@ -292,9 +285,9 @@ public void GetCurrentValues_WithModelStateEntryAndViewData_ReturnsModelStateEnt } [Theory] - [MemberData(nameof(GetCurrentValues_StringAndCollectionData))] + [MemberData(nameof(GetCurrentValues_CollectionData))] public void GetCurrentValues_WithModelStateEntryModelExplorerAndViewData_ReturnsModelStateEntry( - object rawValue, + string[] rawValue, bool allowMultiple, IReadOnlyCollection expected) { @@ -305,12 +298,10 @@ public void GetCurrentValues_WithModelStateEntryModelExplorerAndViewData_Returns var viewContext = GetViewContext(model, metadataProvider); viewContext.ViewData[nameof(Model.Name)] = "ignored ViewData value"; + viewContext.ModelState.SetModelValue(nameof(Model.Name), rawValue, attemptedValue: null); var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(string), "ignored model value"); - var valueProviderResult = new ValueProviderResult(rawValue); - viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult); - // Act var result = htmlGenerator.GetCurrentValues( viewContext, @@ -324,11 +315,11 @@ public void GetCurrentValues_WithModelStateEntryModelExplorerAndViewData_Returns } // rawValue -> expected current values - public static TheoryData GetCurrentValues_StringData + public static TheoryData GetCurrentValues_StringData { get { - return new TheoryData + return new TheoryData { // 1. If given a ModelExplorer, GetCurrentValues does not use ViewData even if expression result is // null. @@ -336,8 +327,8 @@ public static TheoryData GetCurrentValues_StringData // if entry is null. // 3. Otherwise, GetCurrentValue does not fall back anywhere else even if ViewData.Model is null. { null, null }, - { string.Empty, new [] { string.Empty } }, - { "some string", new [] { "some string" } }, + { new string[] { string.Empty }, new [] { string.Empty } }, + { new string[] { "some string" }, new [] { "some string" } }, }; } } @@ -345,7 +336,7 @@ public static TheoryData GetCurrentValues_StringData [Theory] [MemberData(nameof(GetCurrentValues_StringData))] public void GetCurrentValues_WithModelExplorerAndViewData_ReturnsExpressionResult( - string rawValue, + string[] rawValue, IReadOnlyCollection expected) { // Arrange @@ -355,12 +346,10 @@ public void GetCurrentValues_WithModelExplorerAndViewData_ReturnsExpressionResul var viewContext = GetViewContext(model, metadataProvider); viewContext.ViewData[nameof(Model.Name)] = "ignored ViewData value"; + viewContext.ModelState.SetModelValue(nameof(Model.Name), rawValue, attemptedValue: null); var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(string), rawValue); - var valueProviderResult = new ValueProviderResult(rawValue: null); - viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult); - // Act var result = htmlGenerator.GetCurrentValues( viewContext, @@ -375,7 +364,7 @@ public void GetCurrentValues_WithModelExplorerAndViewData_ReturnsExpressionResul [Theory] [MemberData(nameof(GetCurrentValues_StringData))] public void GetCurrentValues_WithViewData_ReturnsViewDataEntry( - object rawValue, + string[] rawValue, IReadOnlyCollection expected) { // Arrange @@ -385,9 +374,7 @@ public void GetCurrentValues_WithViewData_ReturnsViewDataEntry( var viewContext = GetViewContext(model, metadataProvider); viewContext.ViewData[nameof(Model.Name)] = rawValue; - - var valueProviderResult = new ValueProviderResult(rawValue: null); - viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult); + viewContext.ModelState.SetModelValue(nameof(Model.Name), rawValue, attemptedValue: null); // Act var result = htmlGenerator.GetCurrentValues( @@ -402,17 +389,15 @@ public void GetCurrentValues_WithViewData_ReturnsViewDataEntry( [Theory] [MemberData(nameof(GetCurrentValues_StringData))] - public void GetCurrentValues_WithModel_ReturnsModel(string rawValue, IReadOnlyCollection expected) + public void GetCurrentValues_WithModel_ReturnsModel(string[] rawValue, IReadOnlyCollection expected) { // Arrange var metadataProvider = new TestModelMetadataProvider(); var htmlGenerator = GetGenerator(metadataProvider); - var model = new Model { Name = rawValue }; + var model = new Model { Name = rawValue?[0] }; var viewContext = GetViewContext(model, metadataProvider); - - var valueProviderResult = new ValueProviderResult(rawValue: null); - viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult); + viewContext.ModelState.SetModelValue(nameof(Model.Name), rawValue, attemptedValue: null); // Act var result = htmlGenerator.GetCurrentValues( @@ -467,13 +452,11 @@ public void GetCurrentValues_CollectionWithModelExplorerAndViewData_ReturnsExpre var viewContext = GetViewContext(model, metadataProvider); viewContext.ViewData[nameof(Model.Collection)] = new[] { "ignored ViewData value" }; + viewContext.ModelState.SetModelValue(nameof(Model.Collection), rawValue, attemptedValue: null); var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(List), new List(rawValue)); - var valueProviderResult = new ValueProviderResult(rawValue: null); - viewContext.ModelState.SetModelValue(nameof(Model.Collection), valueProviderResult); - // Act var result = htmlGenerator.GetCurrentValues( viewContext, @@ -488,7 +471,7 @@ public void GetCurrentValues_CollectionWithModelExplorerAndViewData_ReturnsExpre [Theory] [MemberData(nameof(GetCurrentValues_StringCollectionData))] public void GetCurrentValues_CollectionWithViewData_ReturnsViewDataEntry( - object[] rawValue, + string[] rawValue, IReadOnlyCollection expected) { // Arrange @@ -498,9 +481,7 @@ public void GetCurrentValues_CollectionWithViewData_ReturnsViewDataEntry( var viewContext = GetViewContext(model, metadataProvider); viewContext.ViewData[nameof(Model.Collection)] = rawValue; - - var valueProviderResult = new ValueProviderResult(rawValue: null); - viewContext.ModelState.SetModelValue(nameof(Model.Collection), valueProviderResult); + viewContext.ModelState.SetModelValue(nameof(Model.Collection), rawValue, attemptedValue: null); // Act var result = htmlGenerator.GetCurrentValues( @@ -526,9 +507,10 @@ public void GetCurrentValues_CollectionWithModel_ReturnsModel( model.Collection.AddRange(rawValue); var viewContext = GetViewContext(model, metadataProvider); - - var valueProviderResult = new ValueProviderResult(rawValue: null); - viewContext.ModelState.SetModelValue(nameof(Model.Collection), valueProviderResult); + viewContext.ModelState.SetModelValue( + nameof(Model.Collection), + rawValue, + attemptedValue: null); // Act var result = htmlGenerator.GetCurrentValues( @@ -637,9 +619,10 @@ public void GetCurrentValues_ValueConvertedAsExpected( var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var htmlGenerator = GetGenerator(metadataProvider); var viewContext = GetViewContext(model: null, metadataProvider: metadataProvider); - - var valueProviderResult = new ValueProviderResult(rawValue); - viewContext.ModelState.SetModelValue(propertyName, valueProviderResult); + viewContext.ModelState.SetModelValue( + propertyName, + new string[] { rawValue.ToString() }, + attemptedValue: null); // Act var result = htmlGenerator.GetCurrentValues( diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperCheckboxTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperCheckboxTest.cs index c0816f83b3..74e7ffe227 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperCheckboxTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperCheckboxTest.cs @@ -134,9 +134,9 @@ public void CheckBoxUsesAttemptedValueFromModelState() @"" + @""); - var valueProviderResult = new ValueProviderResult("false", "false", CultureInfo.InvariantCulture); + var helper = DefaultTemplatesUtilities.GetHtmlHelper(GetTestModelViewData()); - helper.ViewData.ModelState.SetModelValue("Property1", valueProviderResult); + helper.ViewData.ModelState.SetModelValue("Property1", new string[] { "false" }, "false"); // Act var html = helper.CheckBox("Property1", isChecked: null, htmlAttributes: null); @@ -438,8 +438,7 @@ public void CheckBoxForWithNullContainer_TreatsBooleanAsFalse() @""); var viewData = GetTestModelViewData(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); - var valueProviderResult = new ValueProviderResult("false", "false", CultureInfo.InvariantCulture); - viewData.ModelState.SetModelValue("Property1", valueProviderResult); + viewData.ModelState.SetModelValue("Property1", new string[] { "false" }, "false"); // Act var html = helper.CheckBoxFor(m => m.Property1, htmlAttributes: null); @@ -533,9 +532,7 @@ public void CheckBoxFor_UsesModelStateAttemptedValue(string attemptedValue, stri var viewData = GetTestModelViewData(); var helper = DefaultTemplatesUtilities.GetHtmlHelper(viewData); - var valueProviderResult = - new ValueProviderResult(attemptedValue, attemptedValue, CultureInfo.InvariantCulture); - viewData.ModelState.SetModelValue("Property1", valueProviderResult); + viewData.ModelState.SetModelValue("Property1", new string[] { attemptedValue }, attemptedValue); // Act var html = helper.CheckBoxFor(m => m.Property1, htmlAttributes: null); diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperDisplayTextTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperDisplayTextTest.cs index ad55fbf9e0..73033de6fa 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperDisplayTextTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperDisplayTextTest.cs @@ -261,10 +261,8 @@ public void DisplayText_IgnoresModelStateEntry_ReturnsViewDataEntry() viewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix"; var modelState = new ModelState(); - modelState.Value = new ValueProviderResult( - rawValue: new string[] { "Attempted name value" }, - attemptedValue: "Attempted name value", - culture: CultureInfo.InvariantCulture); + modelState.RawValue = new string[] { "Attempted name value" }; + modelState.AttemptedValue = "Attempted name value"; viewData.ModelState["FieldPrefix.Name"] = modelState; // Act @@ -288,10 +286,8 @@ public void DisplayTextFor_IgnoresModelStateEntry() viewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix"; var modelState = new ModelState(); - modelState.Value = new ValueProviderResult( - rawValue: new string[] { "Attempted name value" }, - attemptedValue: "Attempted name value", - culture: CultureInfo.InvariantCulture); + modelState.RawValue = new string[] { "Attempted name value" }; + modelState.AttemptedValue = "Attempted name value"; viewData.ModelState["FieldPrefix.Name"] = modelState; // Act diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperHiddenTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperHiddenTest.cs index 2be4e6818b..7e18011e5b 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperHiddenTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperHiddenTest.cs @@ -874,7 +874,8 @@ private static ModelState GetModelState(string value) { return new ModelState { - Value = new ValueProviderResult(value, value, CultureInfo.InvariantCulture) + RawValue = new string[] { value }, + AttemptedValue = value, }; } diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperPasswordTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperPasswordTest.cs index 48d187f0de..5492cded5b 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperPasswordTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperPasswordTest.cs @@ -371,7 +371,8 @@ private static ModelState GetModelState(string value) { return new ModelState { - Value = new ValueProviderResult(value, value, CultureInfo.InvariantCulture) + RawValue = new string[] { value }, + AttemptedValue = value, }; } diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperSelectTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperSelectTest.cs index 689daf0a9e..ecd0dbea25 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperSelectTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperSelectTest.cs @@ -410,18 +410,18 @@ public void DropDownListNotInTemplate_GetsModelStateEntry() // Arrange var expectedHtml = GetExpectedSelectElement(SelectSources.ModelStateEntry, allowMultiple: false); - var entryResult = new ValueProviderResult( - SelectSources.ModelStateEntry, - SelectSources.ModelStateEntry.ToString(), - culture: null); - var entryResultWithPrefix = new ValueProviderResult( - SelectSources.ModelStateEntryWithPrefix, - SelectSources.ModelStateEntryWithPrefix.ToString(), - culture: null); var modelState = new ModelStateDictionary { - ["Property1"] = new ModelState { Value = entryResult }, - ["Prefix.Property1"] = new ModelState { Value = entryResultWithPrefix }, + ["Property1"] = new ModelState + { + RawValue = new string[] { SelectSources.ModelStateEntry.ToString() }, + AttemptedValue = SelectSources.ModelStateEntry.ToString() + }, + ["Prefix.Property1"] = new ModelState + { + RawValue = new string[] { SelectSources.ModelStateEntryWithPrefix.ToString() }, + AttemptedValue = SelectSources.ModelStateEntryWithPrefix.ToString() + }, }; var provider = TestModelMetadataProvider.CreateDefaultProvider(); @@ -451,18 +451,18 @@ public void DropDownListInTemplate_GetsModelStateEntry() SelectSources.ModelStateEntryWithPrefix, allowMultiple: false); - var entryResult = new ValueProviderResult( - SelectSources.ModelStateEntry, - SelectSources.ModelStateEntry.ToString(), - culture: null); - var entryResultWithPrefix = new ValueProviderResult( - SelectSources.ModelStateEntryWithPrefix, - SelectSources.ModelStateEntryWithPrefix.ToString(), - culture: null); var modelState = new ModelStateDictionary { - ["Property1"] = new ModelState { Value = entryResult }, - ["Prefix.Property1"] = new ModelState { Value = entryResultWithPrefix }, + ["Property1"] = new ModelState + { + RawValue = new string[] { SelectSources.ModelStateEntry.ToString() }, + AttemptedValue = SelectSources.ModelStateEntry.ToString() + }, + ["Prefix.Property1"] = new ModelState + { + RawValue = new string[] { SelectSources.ModelStateEntryWithPrefix.ToString() }, + AttemptedValue = SelectSources.ModelStateEntryWithPrefix.ToString() + }, }; var provider = TestModelMetadataProvider.CreateDefaultProvider(); @@ -821,18 +821,18 @@ public void ListBoxNotInTemplate_GetsModelStateEntry() // Arrange var expectedHtml = GetExpectedSelectElement(SelectSources.ModelStateEntry, allowMultiple: true); - var entryResult = new ValueProviderResult( - SelectSources.ModelStateEntry, - SelectSources.ModelStateEntry.ToString(), - culture: null); - var entryResultWithPrefix = new ValueProviderResult( - SelectSources.ModelStateEntryWithPrefix, - SelectSources.ModelStateEntryWithPrefix.ToString(), - culture: null); var modelState = new ModelStateDictionary { - ["Property1"] = new ModelState { Value = entryResult }, - ["Prefix.Property1"] = new ModelState { Value = entryResultWithPrefix }, + ["Property1"] = new ModelState + { + RawValue = new string[] { SelectSources.ModelStateEntry.ToString() }, + AttemptedValue = SelectSources.ModelStateEntry.ToString() + }, + ["Prefix.Property1"] = new ModelState + { + RawValue = new string[] { SelectSources.ModelStateEntryWithPrefix.ToString() }, + AttemptedValue = SelectSources.ModelStateEntryWithPrefix.ToString() + }, }; var provider = TestModelMetadataProvider.CreateDefaultProvider(); @@ -862,18 +862,18 @@ public void ListBoxInTemplate_GetsModelStateEntry() SelectSources.ModelStateEntryWithPrefix, allowMultiple: true); - var entryResult = new ValueProviderResult( - SelectSources.ModelStateEntry, - SelectSources.ModelStateEntry.ToString(), - culture: null); - var entryResultWithPrefix = new ValueProviderResult( - SelectSources.ModelStateEntryWithPrefix, - SelectSources.ModelStateEntryWithPrefix.ToString(), - culture: null); var modelState = new ModelStateDictionary { - ["Property1"] = new ModelState { Value = entryResult }, - ["Prefix.Property1"] = new ModelState { Value = entryResultWithPrefix }, + ["Property1"] = new ModelState + { + RawValue = new string[] { SelectSources.ModelStateEntry.ToString() }, + AttemptedValue = SelectSources.ModelStateEntry.ToString() + }, + ["Prefix.Property1"] = new ModelState + { + RawValue = new string[] { SelectSources.ModelStateEntryWithPrefix.ToString() }, + AttemptedValue = SelectSources.ModelStateEntryWithPrefix.ToString() + }, }; var provider = TestModelMetadataProvider.CreateDefaultProvider(); diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperValueTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperValueTest.cs index 88f1654eca..7dc8b88c76 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperValueTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperValueTest.cs @@ -164,17 +164,13 @@ public void ValueHelpersWithErrorsGetValueFromModelState() viewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix"; var modelState = new ModelState(); - modelState.Value = new ValueProviderResult( - rawValue: new string[] { "StringPropertyRawValue" }, - attemptedValue: "StringPropertyAttemptedValue", - culture: CultureInfo.InvariantCulture); + modelState.AttemptedValue = "StringPropertyAttemptedValue"; + modelState.RawValue = new string[] { "StringPropertyRawValue" }; viewData.ModelState["FieldPrefix.StringProperty"] = modelState; modelState = new ModelState(); - modelState.Value = new ValueProviderResult( - rawValue: new string[] { "ModelRawValue" }, - attemptedValue: "ModelAttemptedValue", - culture: CultureInfo.InvariantCulture); + modelState.AttemptedValue = "ModelAttemptedValue"; + modelState.RawValue = new string[] { "ModelRawValue" }; viewData.ModelState["FieldPrefix"] = modelState; // Act & Assert @@ -225,10 +221,8 @@ public void ValueHelpersDoNotEncodeValue() viewData["StringProperty"] = "ViewDataValue <\"\">"; var modelState = new ModelState(); - modelState.Value = new ValueProviderResult( - rawValue: new string[] { "ObjectPropertyRawValue <\"\">" }, - attemptedValue: "ObjectPropertyAttemptedValue <\"\">", - culture: CultureInfo.InvariantCulture); + modelState.AttemptedValue = "ObjectPropertyAttemptedValue <\"\">"; + modelState.RawValue = new string[] { "ObjectPropertyRawValue <\"\">" }; viewData.ModelState["ObjectProperty"] = modelState; // Act & Assert diff --git a/test/WebSites/ModelBindingWebSite/Controllers/ModelBinderAttribute_ProductController.cs b/test/WebSites/ModelBindingWebSite/Controllers/ModelBinderAttribute_ProductController.cs index 55c6418589..309b2502b0 100644 --- a/test/WebSites/ModelBindingWebSite/Controllers/ModelBinderAttribute_ProductController.cs +++ b/test/WebSites/ModelBindingWebSite/Controllers/ModelBinderAttribute_ProductController.cs @@ -99,7 +99,7 @@ public async Task BindModelAsync(ModelBindingContext binding bindingContext.ModelName + "." + "productId"; var value = await bindingContext.ValueProvider.GetValueAsync(key); - model.ProductId = (int)value.ConvertTo(typeof(int)); + model.ProductId = value.ConvertTo(); var validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value); diff --git a/test/WebSites/ModelBindingWebSite/Controllers/TryUpdateModelController.cs b/test/WebSites/ModelBindingWebSite/Controllers/TryUpdateModelController.cs index 7c8d5c0c3f..e37f0ac8f4 100644 --- a/test/WebSites/ModelBindingWebSite/Controllers/TryUpdateModelController.cs +++ b/test/WebSites/ModelBindingWebSite/Controllers/TryUpdateModelController.cs @@ -218,7 +218,7 @@ public Task ContainsPrefixAsync(string prefix) public Task GetValueAsync(string key) { - return Task.FromResult(null); + return Task.FromResult(ValueProviderResult.None); } } } diff --git a/test/WebSites/ValueProvidersWebSite/CustomValueProviderFactory.cs b/test/WebSites/ValueProvidersWebSite/CustomValueProviderFactory.cs index 45b88ebd06..f41657ff43 100644 --- a/test/WebSites/ValueProvidersWebSite/CustomValueProviderFactory.cs +++ b/test/WebSites/ValueProvidersWebSite/CustomValueProviderFactory.cs @@ -31,7 +31,7 @@ public Task ContainsPrefixAsync(string prefix) public Task GetValueAsync(string key) { var value = "custom-value-provider-value"; - var result = new ValueProviderResult(value, value, CultureInfo.CurrentCulture); + var result = new ValueProviderResult(value, CultureInfo.CurrentCulture); return Task.FromResult(result); } }