Expanding ValueProviderResult to support more data types

* Reviving tests for ValueProviderResult
This commit is contained in:
Pranav K 2014-04-08 07:27:54 -07:00
parent ea3e958563
commit c8ede78582
4 changed files with 455 additions and 44 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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)

View File

@ -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
}
}
}