Get parent property from Expression function
Asked Answered
P

1

6

So let's say that I have the following classes:

public class Model {
    public AnotherModel InnerModel {
        get;
        set;
    }
}

public class AnotherModel {
    public String Value{
        get;
        set;
    }
}

Now I have the following function:

public static void Foo<T, U>(Expression<Func<T, U>> func) {
     // Get the property info from func
}

What I would like to do now is the following:

Foo<Model, String>(o => o.InnerModel.Value)

Here comes the problem:

I know that you can fetch the PropertyInfo from the expression func by doing:

PropertyInfo propertyInfo = (PropertyInfo)((MemberExpression)func.Body).Member;

This will get me the PropertyInfo of the Value property. However, I would also like to get information about the parent property, that is the InnerModel property.

What I know so far is that I can do the following:

((MemberExpression)func.Body).Expression

to fetch information of the parent property. However, it doesn't seem to be possible to extract a PropertyInfo from the Expression itself.

Is there some way of actually retrieving the PropertyInfo of the Expression?

Edit: To clarify, and it might be a bad way of attempting it but here goes: I can't use EntityFramework for this, just to make sure that that is understood.

There is a database which I need to communicate with through an API.

This database got the usual relations like the manner: Table Thread UserID -> Users.UserID

This now extract to models. To follow the above example:

class Thread {
    [Reference(USER_USERID)]
    [TableKey(THREAD_USERID)]
    public User user {
        get;set;
    }
}

class User {
     [TableKey(USER_USERID)]
     public int UserId {
         get;set;
     }
}

Now I would like to make queries to this. So I thought "Hey, let's use expressions to simplify for the end user on how to ask for stuff, yay."

So, we could do something like EqualTo(o => o.user.UserId, 1);

However, since the TableKey property differs from the reference key I need to first fetch from the database the userId from the Thread table and then with that Id start asking the User table for information with that id.

Maybe this clarifies the purpose of all this, or maybe it don't.

Potsdam answered 12/11, 2014 at 15:12 Comment(5)
If you try to get the information in this fashion then you're forcing the lambda that you accept to call a property of the property of the parameter, and doing anything else would break your code. What are you actually trying to do with this expression, at a higher level? There's likely a better way of getting the information that you want without being so restrictive of the exact format that the lambda needs to be.Harlin
Then you most certainly do need to solve this in a more generalized fashion. It sounds like you're writing an entire query provider from scratch. That's an extremely involved process that is both very difficult, and very lengthy/tedious. There really isn't any way to describe how to appropriately solve this problem in the scope of an SO answer.Harlin
@Potsdam I don't see where this solution attempts to fit into that problem...? That problem also sounds quite broad, which isn't a good fit for SO.Sonorous
@Potsdam So if you managed to catch my answer before I deleted it, that was a way to get what you wanted. In lieu of the comments, I feel the question should probably be closed off.Sonorous
Edited the post for clarification.Potsdam
S
8

As you have already determined, the body of the expression is a MemberExpression. There are two properties of a MemberExpression that we need to look at.

The first is the Member property. This the MemberInfo being called. In your example this the Value property. The second property that we need to look at is the Expression property. This is what the member expression is being called on. In your example, this is {o.InnerModel}.

{o.InnerModel} is another MemberExpression. The Member is InnerModel, and the Expression is o.

Here is some code to get the chain of MemberInfos

public static void Foo<T, U>(Expression<Func<T, U>> func)
{
    var memberExp = func.Body as MemberExpression;
    while (memberExp != null)
    {
        var memberInfo = memberExp.Member;
        Console.WriteLine(memberInfo.Name);
        memberExp = memberExp.Expression as MemberExpression;
    }
}

When called like this:

Foo<Model, String>(o => o.InnerModel.Value);

it will output:

  • Value
  • InnerModel

This:

Foo<Assembly, int>(a => a.EntryPoint.DeclaringType.AssemblyQualifiedName.Length);

will output:

  • Length
  • AssemblyQualifiedName
  • DeclaringType
  • EntryPoint
Shortchange answered 14/11, 2014 at 7:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.