Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use an Expression<Func> to set a nested property?

So I have some code that sets a property on an object. This code is from an internal validation class that we're using in unit tests. So the code may be supplied something like

private static void SetDeepValue(object targetObject, Expression<Func<string>> propertyToSet, object valueToSet)
        {
            var underlyingProperty = ((PropertyInfo)((MemberExpression)propertyToSet.Body).Member);
            underlyingProperty.SetValue(targetObject, valueToSet);
        }

This code gets used in a unit-test type environment, where we can then make calls like

foreach (string currentTestCaseValue in TestCaseSets)
{
     BusinessObject myCustomer = new BusinessObject();
     SetDeepValue(myCustomer, ()=>myCustomer.FirstName,currentTestCaseValue);
     ValidateBusinessRules(myCustomer);
}

(code simplified for brevity/complexity)

However, now, due to some refactorings, we are left with something like:

foreach (string currentTestCaseValue in TestCaseSets)
    {
         BusinessObject myCustomer = new BusinessObject();
         SetDeepValue(myCustomer, ()=>myCustomer.NameInfo.First,currentTestCaseValue);
         ValidateBusinessRules(myCustomer);
    }

When this code runs, we get the error:

Object does not match target type.

I suspect it is trying to call the First property on the BusinessObject, instead of the NameInfo. How can I modify my code to handle this 'nested' case?

like image 319
GWLlosa Avatar asked Mar 16 '15 19:03

GWLlosa


1 Answers

Here's how you would usually convert string "ColumnName1.ColumnName2" to a lambda expression x => x.ColumnName1.ColumnName2:

Expression<Func<T, object>> ForNestedProperty(string columnName)
{
    // x
    ParameterExpression param = Expression.Parameter(typeof(T), "x");

    // x.ColumnName1.ColumnName2
    Expression property = columnName.Split('.')
                                    .Aggregate<string, Expression>
                                    (param, (c, m) => Expression.Property(c, m));

    // x => x.ColumnName1.ColumnName2
    Expression<Func<T, object>> lambda = Expression.Lambda<Func<T, object>>(
        Expression.Convert(property, typeof(object)), param);
    return lambda;
}

(Copied from here)

like image 78
abatishchev Avatar answered Sep 20 '22 14:09

abatishchev