Expanding ValueProviderResult to support more data types
* Reviving tests for ValueProviderResult
This commit is contained in:
parent
ea3e958563
commit
c8ede78582
|
|
@ -298,6 +298,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return GetString("Validation_ValueNotFound");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot convert value '{0}' to enum type '{1}'.
|
||||
/// </summary>
|
||||
internal static string ValueProviderResult_CannotConvertEnum
|
||||
{
|
||||
get { return GetString("ValueProviderResult_CannotConvertEnum"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot convert value '{0}' to enum type '{1}'.
|
||||
/// </summary>
|
||||
internal static string FormatValueProviderResult_CannotConvertEnum(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ValueProviderResult_CannotConvertEnum"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The parameter conversion from type '{0}' to type '{1}' failed. See the inner exception for more information.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -171,6 +171,9 @@
|
|||
<data name="Validation_ValueNotFound" xml:space="preserve">
|
||||
<value>A value is required but was not present in the request.</value>
|
||||
</data>
|
||||
<data name="ValueProviderResult_CannotConvertEnum" xml:space="preserve">
|
||||
<value>Cannot convert value '{0}' to enum type '{1}'.</value>
|
||||
</data>
|
||||
<data name="ValueProviderResult_ConversionThrew" xml:space="preserve">
|
||||
<value>The parameter conversion from type '{0}' to type '{1}' failed. See the inner exception for more information.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -71,6 +71,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return value;
|
||||
}
|
||||
|
||||
// In case of a Nullable object, we try again with its underlying type.
|
||||
var underlyingType = Nullable.GetUnderlyingType(destinationType);
|
||||
if (underlyingType != null)
|
||||
{
|
||||
destinationType = underlyingType;
|
||||
}
|
||||
|
||||
// if this is a user-input value but the user didn't type anything, return no value
|
||||
var valueAsString = value as string;
|
||||
|
||||
|
|
@ -79,56 +86,63 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return null;
|
||||
}
|
||||
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return Convert.ToString(value, culture);
|
||||
}
|
||||
|
||||
if (destinationType == typeof(int))
|
||||
{
|
||||
return Convert.ToInt32(value);
|
||||
return Convert.ToInt32(value, culture);
|
||||
}
|
||||
else if (destinationType == typeof(bool))
|
||||
|
||||
if (destinationType == typeof(long))
|
||||
{
|
||||
return Boolean.Parse(value.ToString());
|
||||
return Convert.ToInt64(value, culture);
|
||||
}
|
||||
else if (destinationType == typeof(string))
|
||||
|
||||
if (destinationType == typeof(float))
|
||||
{
|
||||
return Convert.ToString(value);
|
||||
return Convert.ToSingle(value, culture);
|
||||
}
|
||||
string message = Resources.FormatValueProviderResult_NoConverterExists(value.GetType(), destinationType);
|
||||
|
||||
if (destinationType == typeof(double))
|
||||
{
|
||||
return Convert.ToDouble(value, culture);
|
||||
}
|
||||
|
||||
if (destinationType == typeof(decimal))
|
||||
{
|
||||
return Convert.ToDecimal(value, culture);
|
||||
}
|
||||
|
||||
if (destinationType == typeof(bool))
|
||||
{
|
||||
return Convert.ToBoolean(value, culture);
|
||||
}
|
||||
|
||||
if (destinationType.GetTypeInfo().IsEnum)
|
||||
{
|
||||
// EnumConverter cannot convert integer, so we verify manually
|
||||
if ((value is int))
|
||||
{
|
||||
if (Enum.IsDefined(destinationType, value))
|
||||
{
|
||||
return Enum.ToObject(destinationType, (int)value);
|
||||
}
|
||||
|
||||
throw new FormatException(
|
||||
Resources.FormatValueProviderResult_CannotConvertEnum(value,
|
||||
destinationType));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Enum.Parse(destinationType, valueAsString);
|
||||
}
|
||||
}
|
||||
|
||||
var message = Resources.FormatValueProviderResult_NoConverterExists(value.GetType(), destinationType);
|
||||
throw new InvalidOperationException(message);
|
||||
|
||||
// TODO: Revive once we get TypeConverters
|
||||
//TypeConverter converter = TypeDescriptor.GetConverter(destinationType);
|
||||
//bool 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)
|
||||
// {
|
||||
// return Enum.ToObject(destinationType, (int)value);
|
||||
// }
|
||||
|
||||
// // In case of a Nullable object, we try again with its underlying type.
|
||||
// Type underlyingType = Nullable.GetUnderlyingType(destinationType);
|
||||
// if (underlyingType != null)
|
||||
// {
|
||||
// return ConvertSimpleType(culture, value, underlyingType);
|
||||
// }
|
||||
|
||||
// throw Error.InvalidOperation(Resources.ValueProviderResult_NoConverterExists, value.GetType(), destinationType);
|
||||
//}
|
||||
|
||||
//try
|
||||
//{
|
||||
// return canConvertFrom
|
||||
// ? converter.ConvertFrom(null, culture, value)
|
||||
// : converter.ConvertTo(null, culture, value, destinationType);
|
||||
//}
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// throw Error.InvalidOperation(ex, Resources.ValueProviderResult_ConversionThrew, value.GetType(), destinationType);
|
||||
//}
|
||||
}
|
||||
|
||||
private static object UnwrapPossibleArrayType(CultureInfo culture, object value, Type destinationType)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using System.Globalization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ValueProviderResultTest
|
||||
{
|
||||
|
|
@ -24,5 +26,381 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
|
||||
Assert.Equal(0, convertedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToCanConvertArraysToSingleElements()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(new int[] { 1, 20, 42 }, "", CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var converted = (string)vpr.ConvertTo(typeof(string));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("1", converted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToCanConvertSingleElementsToArrays()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(42, "", CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var converted = (string[])vpr.ConvertTo(typeof(string[]));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(converted);
|
||||
var result = Assert.Single(converted);
|
||||
Assert.Equal("42", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToCanConvertSingleElementsToSingleElements()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(42, "", CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var converted = (string)vpr.ConvertTo(typeof(string));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(converted);
|
||||
Assert.Equal("42", converted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertingNullStringToNullableIntReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
object original = null;
|
||||
var vpr = new ValueProviderResult(original, "", CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var returned = (int?)vpr.ConvertTo(typeof(int?));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(returned, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertingWhiteSpaceStringToNullableIntReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var original = " ";
|
||||
var vpr = new ValueProviderResult(original, "", CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var returned = (int?)vpr.ConvertTo(typeof(int?));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(returned, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsNullIfArrayElementValueIsNull()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(new string[] { null }, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(int));
|
||||
|
||||
// Assert
|
||||
Assert.Null(outValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsNullIfTryingToConvertEmptyArrayToSingleElement()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(new int[0], "", CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(int));
|
||||
|
||||
// Assert
|
||||
Assert.Null(outValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData(" \t \r\n ")]
|
||||
public void ConvertToReturnsNullIfTrimmedValueIsEmptyString(object value)
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(value, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(int));
|
||||
|
||||
// Assert
|
||||
Assert.Null(outValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsNullIfTrimmedValueIsEmptyString()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(rawValue: null, attemptedValue: null, culture: CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(int[]));
|
||||
|
||||
// Assert
|
||||
Assert.Null(outValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsValueIfArrayElementIsIntegerAndDestinationTypeIsEnum()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(new object[] { 1 }, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(MyEnum));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(outValue, MyEnum.Value1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsValueIfArrayElementIsStringValueAndDestinationTypeIsEnum()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(new object[] { "1" }, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(MyEnum));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(outValue, MyEnum.Value1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsValueIfArrayElementIsStringKeyAndDestinationTypeIsEnum()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(new object[] { "Value1" }, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(MyEnum));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(outValue, MyEnum.Value1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsValueIfElementIsStringAndDestinationIsNullableInteger()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult("12", null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(int?));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(12, outValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsValueIfElementIsStringAndDestinationIsNullableDouble()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult("12.5", null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(double?));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(12.5, outValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsValueIfElementIsDecimalAndDestinationIsNullableInteger()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(12M, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(int?));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(12, outValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsValueIfElementIsDecimalAndDestinationIsNullableDouble()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(12.5M, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(double?));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(12.5, outValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsValueIfElementIsDecimalDoubleAndDestinationIsNullableInteger()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(12.5M, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(int?));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(12, outValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsValueIfElementIsDecimalDoubleAndDestinationIsNullableLong()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(12.5M, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(long?));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(12L, outValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsValueIfArrayElementInstanceOfDestinationType()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(new object[] { "some string" }, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(string));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("some string", outValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(new object[] { new[] { 1, 0 }})]
|
||||
[InlineData(new object[] { new[] {"Value1", "Value0" }})]
|
||||
public void ConvertTo_ConvertsEnumArrays(object value)
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult(value, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = (MyEnum[])vpr.ConvertTo(typeof(MyEnum[]));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, outValue.Length);
|
||||
Assert.Equal(MyEnum.Value1, outValue[0]);
|
||||
Assert.Equal(MyEnum.Value0, outValue[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToReturnsValueIfInstanceOfDestinationType()
|
||||
{
|
||||
// Arrange
|
||||
var original = new[] { "some string" };
|
||||
var vpr = new ValueProviderResult(original, null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act
|
||||
var outValue = vpr.ConvertTo(typeof(string[]));
|
||||
|
||||
// Assert
|
||||
Assert.Same(original, outValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(int), typeof(FormatException))]
|
||||
[InlineData(typeof(double?), typeof(FormatException))]
|
||||
[InlineData(typeof(MyEnum?), typeof(ArgumentException))]
|
||||
public void ConvertToThrowsIfConverterThrows(Type destinationType, Type exceptionType)
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult("this-is-not-a-valid-value", null, CultureInfo.InvariantCulture);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws(exceptionType, () => vpr.ConvertTo(destinationType));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToThrowsIfNoConverterExists()
|
||||
{
|
||||
// Arrange
|
||||
var vpr = new ValueProviderResult("x", null, CultureInfo.InvariantCulture);
|
||||
var destinationType = typeof(MyClassWithoutConverter);
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => vpr.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 vpr = new ValueProviderResult(original, null, new CultureInfo("en-GB"));
|
||||
var frCulture = new CultureInfo("fr-FR");
|
||||
|
||||
// Act
|
||||
var cultureResult = (decimal)vpr.ConvertTo(typeof(decimal), frCulture);
|
||||
var result = (decimal)vpr.ConvertTo(typeof(decimal));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(12.5M, cultureResult);
|
||||
Assert.Equal(125, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CulturePropertyDefaultsToInvariantCulture()
|
||||
{
|
||||
// Arrange
|
||||
var result = new ValueProviderResult(null, null, null);
|
||||
|
||||
// Act & assert
|
||||
Assert.Same(CultureInfo.InvariantCulture, result.Culture);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData("IntrinsicConversionData")]
|
||||
public void ConvertToCanConvertIntrinsics<T>(object initialValue, T expectedValue)
|
||||
{
|
||||
// Arrange
|
||||
var result = new ValueProviderResult(initialValue, "", CultureInfo.InvariantCulture);
|
||||
|
||||
// Act & Assert
|
||||
Assert.Equal(expectedValue, result.ConvertTo(typeof(T)));
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> IntrinsicConversionData
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new object[] { 42, 42M };
|
||||
yield return new object[] { 42, 42L };
|
||||
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[] { (float)42.0, 42 };
|
||||
yield return new object[] { (double)42.0, 42 };
|
||||
}
|
||||
}
|
||||
|
||||
private class MyClassWithoutConverter
|
||||
{
|
||||
}
|
||||
|
||||
private enum MyEnum
|
||||
{
|
||||
Value0 = 0,
|
||||
Value1 = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue