tags:

views:

144

answers:

2

Given an

Expression<Func<T, object>> 

(e.g. x => x.Prop1.SubProp), I want to create a string "Prop1.SubProp" for as deep as necessary.

In the case of a single access (e.g. x => x.Prop1), I can easily do this with:

MemberExpression body = (expression.Body.NodeType == ExpressionType.Convert) ? (MemberExpression)((UnaryExpression)expression.Body).Operand : (MemberExpression)expression.Body;
return body.Member.Name;

However, if there is deeper nesting, e.g. x => x.Prop1.SubProp1, this only gets the most deeply nested name, e.g. "SubProp1" instead of "Prop1.SubProp1"

Is there anyway to access the full property path of a lambda expression?

+4  A: 
public string GetPath<T>(Expression<Func<T, object>> expr)
{
    var stack = new Stack<string>();

    MemberExpression me;
    switch (expr.Body.NodeType)
    {
        case ExpressionType.Convert:
        case ExpressionType.ConvertChecked:
            var ue = expr.Body as UnaryExpression;
            me = ((ue != null) ? ue.Operand : null) as MemberExpression;
            break;
        default:
            me = expr.Body as MemberExpression;
            break;
    }

    while (me != null)
    {
        stack.Push(me.Member.Name);
        me = me.Expression as MemberExpression;
    }

    return string.Join(".", stack.ToArray());
}
LukeH
+1: Great minds think alike! One little thing: don't you think this really ought to be defined `GetPath<T, TProperty>(Expression<Func<T, TProperty>> expr)`? Otherwise you're nearly always going to run into that `Convert`/`ConvertChecked` scenario -- simply because the argument is cast unnecessarily. (Or am I missing something?)
Dan Tao
@Dan: Yep, I agree. In fact, before my edits I had the signature as `GetPath<T,U>(Expression<Func<T,U>> expr)`. I changed it to match the expression shown in the question. I'll leave it as-is, and I suppose the OP can choose which signature they prefer - the code in the method body stays the same either way.
LukeH
+3  A: 

Take a look at my answer to this question.

Pretty much the same as what LukeH posted, with one additional feature:

If you have a type, say, MyClass, with a property MyProperty of type int, you could write this:

Expression<Func<MyClass, object>> e = x => x.MyProperty;

Here the expression e.Body is not a MemberExpression so the simple while (me != null) me = me.Expression as MemberExpression won't work.

The solution is to check additionally if it's a UnaryExpression with NodeType == Convert or ConvertChecked.

There may be other scenarios to account for; but for simple chains of property expressions, this approach works pretty well.

Dan Tao
@Dan: I was just editing my answer to take account of `Convert`/`ConvertChecked` when you edited yours.
LukeH
@LukeH: What can I say? Clearly you know what you're doing ;)
Dan Tao