views:

77

answers:

2

I want to convert a string to an object property value, whose name I have as a string. I am trying to do this like so:

string modelProperty = "Some Property Name";
string value = "SomeValue";
var property = entity.GetType().GetProperty(modelProperty);
if (property != null) {
    property.SetValue(entity, 
        Convert.ChangeType(value, property.PropertyType), null);
}

The problem is this is failing and throwing an Invalid Cast Exception when the property type is a nullable type. This is not the case of the values being unable to be Converted - they will work if I do this manually (e.g. DateTime? d = Convert.ToDateTime(value);) I've seen some similiar questions but still can't get it to work.

+4  A: 

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.

BenAlabaster
Thanks - I'm still missing a step or not understanding something. I am trying to set a property value, why am I getting the object that it is in's Underlying Type? I am also not sure how to get from my code to an extension method like yours. I won't know what the type will be in order to do something like value.Helper<Int32?>().
iboeno
@iboeno - Sorry, was off in a meeting so I couldn't help you connect the dots. Glad you got a solution though.
BenAlabaster
+1  A: 

Untested, but maybe something like this will work:

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

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);
}
LukeH
@LukeH this works, thanks.
iboeno
if you'd used a coallescing operator, you could've avoided the if statement. Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; and safeValue = Convert.ChangeType(value, t); It would've avoided a bunch of extra code there...
BenAlabaster