Convert.ChangeType() fails on Nullable Types

You have to get the underlying type in order to do that...

Try this, I've used it successfully with generics:

//Coalesce to get actual property type...
Type t = property.PropertyType();
t = Nullable.GetUnderlyingType(t) ?? t;

//Coalesce to set the safe value using default(t) or the safe type.
safeValue = value == null ? default(t) : Convert.ChangeType(value, t);

I use it in a number of places in my code, one example is a helper method I use for converting database values in a typesafe manner:

public static T GetValue<T>(this IDataReader dr, string fieldName)
{
    object value = dr[fieldName];

    Type t = typeof(T);
    t = Nullable.GetUnderlyingType(t) ?? t;

    return (value == null || DBNull.Value.Equals(value)) ? 
        default(T) : (T)Convert.ChangeType(value, t);
}

Called using:

string field1 = dr.GetValue<string>("field1");
int? field2 = dr.GetValue<int?>("field2");
DateTime field3 = dr.GetValue<DateTime>("field3");

I wrote a series of blog posts including this at http://www.endswithsaurus.com/2010_07_01_archive.html (Scroll down to the Addendum, @JohnMacintyre actually spotted the bug in my original code which led me down the same path you're on now). I have a couple of small modifications since that post that includes conversion of enum types also so if your property is an Enum you can still use the same method call. Just add a line in to check for enum types and you're off to the races using something like:

if (t.IsEnum)
    return (T)Enum.Parse(t, value);

Normally you'd have some error checking or use TryParse instead of Parse, but you get the picture.


Untested, but maybe something like this will work:

string modelProperty = "Some Property Name";
string value = "Some Value";

var property = entity.GetType().GetProperty(modelProperty);
if (property != null)
{
    Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;

    object safeValue = (value == null) ? null : Convert.ChangeType(value, t);

    property.SetValue(entity, safeValue, null);
}