5

How do I build an expression that will fulfill the following goal:

public object Eval(object rootObj, string propertyString)

eg: Eval(person, "Address.ZipCode") => return person.Address.ZipCode

Expression.PropertyOrField doesn't work because I don't have the type of each intermediate property. I'd like to avoid creating a dependency on a scripting library.

I want to try to use expressions because it would allow me to store a cache of these expression trees as they would be executed several times. I'm aware that it's possible to do this iteratively or recursively with reflection.

Kir
  • 2,905
  • 2
  • 27
  • 44

2 Answers2

10

It sounds like you're looking for something like this:

public object Eval(object root, string propertyString)
{
    var propertyNames = propertyString.Split('.');
    foreach(var prop in propertyNames)
    {
        var property = root.GetType().GetProperty(prop);
        if (property == null)
        {
            throw new Exception(...);
        }

        root = property.GetValue(root, null);
    }

    return root;
}

To create an Expression use this:

public Expression Eval(object root, string propertyString)
{
    var propertyNames = propertyString.Split('.');
    ParameterExpression param = Expression.Parameter(root.GetType, "_");
    Expression property = param;
    foreach(var prop in propertyName)
    {
        property = Expression.PropertyOrField(property, prop);
    }

    return Expression.Lambda(property, param);
}
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • +1 - I was working on something just like this, but you beat me to it. – Bobson May 14 '13 at 13:44
  • This would not work, since in the example `Person.Address.Zipcode` has root object `Person`, which does not have `Zipcode` as a property -- that is a property of the class/struct `Address`. So I think recursive evaluation is necessary. – John Willemse May 14 '13 at 13:46
  • 2
    @JohnWillemse - `root` changes on each iteration, so on the second pass, it would be looking at `Address` for `Zipcode`. – Bobson May 14 '13 at 13:48
  • I inquired about expressions specifically. I clarified why in my edit. Thanks. – Kir May 14 '13 at 13:49
  • @Bobson I stand corrected! I failed to see that. `Address` must be a property though, not a field, then it will fail. – John Willemse May 14 '13 at 13:54
3

Here's a recursive version of p.s.w.g's code, working with Expressions.

public Expression Eval(Expression expression, string property)
{
    var split = property.Split('.');
    if (split.Length == 1)
    {
        return Expression.PropertyOrField(expression, property);
    }
    else
    {
        return Eval(Expression.PropertyOrField(expression, split[0]), property.Replace(split[0] + ".", ""));
    }
}
Bobson
  • 13,498
  • 5
  • 55
  • 80
  • +1 But I don't think you need to split the string every time if you use recursion. You can just use `property.IndexOf('.')` and `property.Substring(...)` – p.s.w.g May 14 '13 at 13:58
  • @p.s.w.g - Entirely true, and a better way to do it. I was just being lazy. – Bobson May 14 '13 at 16:10