29

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?


3 답변


34

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());
}


  • +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
  • Any idea how to modify this to work with an expression that contains index (ie square bracket) operator(s)? - joelmdev

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.


  • 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

0

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

Linked


Related

Latest