How to assign a value via Expression?

2020-07-06 04:12发布

This would be very simple if I were able to assign via a Lambda expression (below)

//An expression tree cannot contain an assignment operator
Expression<Func<ComplexObj, object>> expression = obj => obj.Contacts[0].FirstName = "Tim";

This code above is invalid due to the assignment operator. I need to pass a lambda expression in order to identify the property in the complex object that needs to be set. In some cases the complex object has List and therefor duplicate object types and names which is why I need the lambda to explicitly reference the field in the object to be updated.

I am able to retrieve the value using the following, no problem. But I am not sure how to use this same logic to set the value, I came across Expression.Assign and believe this may be the solution.

Expression<Func<ComplexObj, object>> expression = obj => obj.Contacts[0].FirstName;
var result = FindByProperty(expression);

public static string FindByProperty(Expression<Func<Contract, object>> propertyRefExpr)
{
    ComplexObj obj = new ComplexObj();
    Contact myContact = new Contact();
    myContact.FirstName = "Allen";
    obj.Contacts = new List<Contact>{myContact};
    return propertyRefExpr.Compile().Invoke(obj);
}

Update:

"passing a property assignment to a method as an expression tree..."

Using the SetValue method with ParentTypeA, Value will not work. (below code)

Expression<Func<ComplexObj, object>> expression = obj => obj.Contacts[0].FirstName;
obj.AssignNewValue(expression, firstName);

public static void AssignNewValue(this ComplexObj obj, Expression<Func<ComplexObj, object>> expression, object value)
{
    var propertyInfo = (PropertyInfo)((MemberExpression)expression.Body).Member;
    propertyInfo.SetValue(obj, value, null);
}

2条回答
beautiful°
2楼-- · 2020-07-06 04:54

I ended up using the following solution. Cheers

ComplexObj obj = new ComplexObj();
Expression<Func<ComplexObj, object>> expression = obj => obj.Contacts[index].FirstName;
obj.AssignNewValue(expression, firstName);

public static void AssignNewValue(this ComplexObj obj, Expression<Func<ComplexObj, object>> expression, object value)
{
    ParameterExpression valueParameterExpression = Expression.Parameter(typeof(object));
    Expression targetExpression = expression.Body is UnaryExpression ? ((UnaryExpression)expression.Body).Operand : expression.Body;

    var newValue = Expression.Parameter(expression.Body.Type);
    var assign = Expression.Lambda<Action<ComplexObj, object>>
                (
                    Expression.Assign(targetExpression, Expression.Convert(valueParameterExpression, targetExpression.Type)),
                    expression.Parameters.Single(),
                    valueParameterExpression
                );

    assign.Compile().Invoke(obj, value);
}
查看更多
够拽才男人
3楼-- · 2020-07-06 05:01

The linked-to question is probably the "right" answer, but just for the sake of completeness, you could do something more like this...it does follow the "tell, don't ask" methodology a bit better, although I can't say I like the implementation...

void Main()
{
    Expression<Func<ComplexObj, object>> expression = 
      obj => obj.Contacts[0].SetFirstName("Tim");       
}

public class ComplexObj
{
    public ComplexObj() { Contacts = new List<SimpleObj>(); }
    public List<SimpleObj> Contacts {get; private set;}
}
public class SimpleObj
{
    public string FirstName {get; private set;}
    public SimpleObj SetFirstName(string name) { this.FirstName = name; return this; }
}
查看更多
登录 后发表回答