31

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?

Nathan
  • 10,593
  • 10
  • 63
  • 87
  • Take a look at MemberExpression.Expression. You can probably use that to recursively walk the expression tree as long as they are all MemberExpression instances. – Dan Bryant Jun 15 '10 at 23:48
  • I had a hard time choosing an "accepted answer" both were good. Anybody viewing this question, I recommend looking at the answers from both LukeH and Dan Tao. – Nathan Jun 16 '10 at 02:45
  • possible duplicate of [Get the property, as a string, from an Expression>](http://stackoverflow.com/questions/2789504/get-the-property-as-a-string-from-an-expressionfunctmodel-tproperty) – nawfal Oct 11 '13 at 18:58

3 Answers3

38
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
  • 263,068
  • 57
  • 365
  • 409
  • 1
    +1: Great minds think alike! One little thing: don't you think this really ought to be defined `GetPath(Expression> 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 Jun 16 '10 at 00:37
  • 1
    @Dan: Yep, I agree. In fact, before my edits I had the signature as `GetPath(Expression> 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 Jun 16 '10 at 00:47
  • 1
    Any idea how to modify this to work with an expression that contains index (ie square bracket) operator(s)? – joelmdev Oct 15 '14 at 20:49
6

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.

Community
  • 1
  • 1
Dan Tao
  • 125,917
  • 54
  • 300
  • 447
1

You can use a project I have created to convert lambda to javascript: lambda2js

When using only properties and indexers, the result should be exactly what you need.

Example 1: Single property path

Expression<Func<MyClass, object>> expr = x => x.Phone;
var js = expr.CompileToJavascript();
// returns: Phone

Example 2: Path containing indexer of a string dictionary

Expression<Func<MyClass, object>> expr = x => x.PhonesByName["Miguel"];
var js = expr.CompileToJavascript();
// returns: PhonesByName["Miguel"]

Example 3: Complex path containing indexers and multiple levels

Expression<Func<MyClass, object>> expr = x => x.SomeProp["Miguel"].Subprop[0].A.B;
var js = expr.CompileToJavascript();
// returns: SomeProp["Miguel"].Subprop[0].A.B
Miguel Angelo
  • 23,796
  • 16
  • 59
  • 82