diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Properties/Resources.Designer.cs
index d1a85bbbd4..445368e1aa 100644
--- a/src/Microsoft.AspNet.Mvc.ModelBinding/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Properties/Resources.Designer.cs
@@ -298,6 +298,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return GetString("Validation_ValueNotFound");
}
+ ///
+ /// 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. See the inner exception for more information.
///
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Resources.resx b/src/Microsoft.AspNet.Mvc.ModelBinding/Resources.resx
index 5d6251b5b4..70fce8fb43 100644
--- a/src/Microsoft.AspNet.Mvc.ModelBinding/Resources.resx
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Resources.resx
@@ -171,6 +171,9 @@
A value is required but was not present in the request.
+
+ Cannot convert value '{0}' to enum type '{1}'.
+
The parameter conversion from type '{0}' to type '{1}' failed. See the inner exception for more information.
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/ValueProviderResult.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/ValueProviderResult.cs
index 73d4335216..e1e797d6ed 100644
--- a/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/ValueProviderResult.cs
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/ValueProviderResult.cs
@@ -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)
diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/ValueProviders/ValueProviderResultTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/ValueProviders/ValueProviderResultTest.cs
index db9f63c5d0..a173ee2467 100644
--- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/ValueProviders/ValueProviderResultTest.cs
+++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/ValueProviders/ValueProviderResultTest.cs
@@ -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(() => 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(object initialValue, T expectedValue)
+ {
+ // Arrange
+ var result = new ValueProviderResult(initialValue, "", CultureInfo.InvariantCulture);
+
+ // Act & Assert
+ Assert.Equal(expectedValue, result.ConvertTo(typeof(T)));
+ }
+
+ public static IEnumerable