4

I have successfully assigned values to Properties and nested Properties using this function

private static void AssignValueToProperty(ObjectAccessor accessor, object value, string propertyLambdaString)
{
    var index =  propertyLambdaString.IndexOf('.');

    if (index == -1)
    {
        accessor[propertyLambdaString] = value;
        // problem above: throws Exception if assigning value to Nullable<T>
    }
    else
    {
        var property = propertyLambdaString.Substring(0, index);
        accessor = ObjectAccessor.Create(accessor[property]);

        AssignValueToProperty(accessor, value, propertyLambdaString.Substring(index + 1));
    }
}

However, the assignment throws an InvalidCastException. How to assign nullable values instead using FastMember? For example

public class A
{
  public double? SomeValue {get; set;}
}

...
var a = new A();
var accessor = ObjectAccessor.Create(a);
accessor["SomeValue"] = 100; // throws Exception, when assigning 100.0 it works???
Beachwalker
  • 7,685
  • 6
  • 52
  • 94
  • I don't understand the question. Executing the code at the bottom of your question (fixing the missing semicolon after `set`) seems to work just fine, and copying the accessor statement and assigning `null` also works, the object referenced by `a` has a `SomeValue` value of 0.1 and then it is `null`. What is the problem here? Can you post a [mcve]? – Lasse V. Karlsen Oct 28 '16 at 13:57
  • @LasseV.Karlsen Now I see... change the value to 100, does not automatically casted to the correct type. Source = "FastMember_dynamic" – Beachwalker Oct 28 '16 at 14:10
  • StackTrace = " bei FastMember_dynamic.A_2.set_Item(Object , String , Object )\r\n bei FastMember.ObjectAccessor.TypeAccessorWrapper.set_Item(String name, Object value)\r\n bei Client.Data.Access.PropertyUpdater`1.Update(String propertyLambdaString, Object v... – Beachwalker Oct 28 '16 at 14:11
  • 1
    Well, then that is a problem that either FastMember has decided it won't tackle (conversion, not unboxing), or it is a bug in FastMember. – Lasse V. Karlsen Oct 28 '16 at 14:33
  • 1
    You're not "converting" an object containing an int to a double, you're "unboxing" the int, there's no conversion involved. As such the assignment likely ends up trying to unbox a double from a boxed int. – Lasse V. Karlsen Oct 28 '16 at 14:34
  • How to get the type then if the current value is null? Can't call GetType() here. – Beachwalker Oct 28 '16 at 14:36
  • You don't need to handle that part, you only need to handle non-null-but-wrong-type, `null` is OK. – Lasse V. Karlsen Oct 28 '16 at 14:39
  • Hmm, I mean nullable as the property (current value), not the value to be assigned. I cant use Convert methods if I don't know the type of the target property (how to obtain from null?). – Beachwalker Oct 28 '16 at 14:44
  • You can't, you need FastMember to co-operate with you, if it can't do that then you can't use it. – Lasse V. Karlsen Oct 28 '16 at 14:47

2 Answers2

6

FastMember has nothing related to type conversion within it's toolbox, so this is the solution I came up with as Extension Method for FastMember ObjectAccessor:

public static class FastMemberExtensions
{
    public static void AssignValueToProperty(this ObjectAccessor accessor, string propertyName, object value)
    {
        var index = propertyName.IndexOf('.');

        if (index == -1)
        {
            var targetType = Expression.Parameter(accessor.Target.GetType());
            var property = Expression.Property(targetType, propertyName);

            var type = property.Type;
            type = Nullable.GetUnderlyingType(type) ?? type;
            value = value == null ? GetDefault(type) : Convert.ChangeType(value, type);
            accessor[propertyName] = value;
        }
        else
        {
            accessor = ObjectAccessor.Create(accessor[propertyName.Substring(0, index)]);
            AssignValueToProperty(accessor, propertyName.Substring(index + 1), value);
        }
    }

    private static object GetDefault(Type type)
    {
        return type.IsValueType ? Activator.CreateInstance(type) : null;
    }
}

Can be called this way:

var accessor = ObjectAccessor.Create(t); // t is instance of SomeType
accessor.AssignValueToProperty("Nested.Property", value); // t.Nested.Property = value
Beachwalker
  • 7,685
  • 6
  • 52
  • 94
3

FastMember is not going to convert types for you. 100 is an int literal, but the target property is of type decimal?. There is no implicit conversion from int to decimal? (or decimal). 100.0 is a Double literal which implicitly converts to decimal?, and thus the assignment will succeed.

public class A
{
    public double? SomeValue { get; set; }
}

public static class Sample
{
    public static void Go()
    {
        var a = new A();
        var accessor = ObjectAccessor.Create(a);
        accessor["SomeValue"] = 100.0; // succeeds
        accessor["SomeValue"] = 100M; // succeeds
        accessor["SomeValue"] = null; // succeeds
        accessor["SomeValue"] = 100; // throws, can't convert from int to decimal?
    }
}

If there is not an implicit conversion, you'll have to perform necessary conversions in your code.

Implicit conversions:

https://msdn.microsoft.com/en-us/library/y5b434w4.aspx

Explicit conversions:

https://msdn.microsoft.com/en-us/library/yht2cx7b.aspx

Jeffrey Patterson
  • 2,342
  • 1
  • 13
  • 9