I have an expression helper to help getting values from object hierarchy
public static Func<T, object> GetMemberExpressionFunc<T>(string expressionString)
{
if (string.IsNullOrEmpty(expressionString))
{
throw new InvalidOperationException("invalid Expression");
}
var parameter = Expression.Parameter(typeof(T));
Expression memberExpression = parameter;
var tokens = expressionString.Split('.');
memberExpression = tokens.Aggregate(memberExpression, Expression.PropertyOrField);
var convertExpression = Expression.Convert(memberExpression, typeof(object));
return Expression.Lambda<Func<T, object>>(convertExpression, parameter)
.Compile();
}
Usage
public class A
{
public B BObj { get; set; }
}
public class B
{
public string Name { get; set; }
}
static void Main(string[] args)
{
var obj = new A {BObj = new B {Name ="Test"}};
var obj2 = new A ();
var exp = ExpressionHelper.GetMemberExpressionFunc<A>("BObj.Name");
var test1 = exp(obj);// test1 is "Test"
var test2 = exp(obj2); //throws because BObj is null
}
I want the expression to return null if any property in the hierarchy is null instead of throwing exception. Is it possible to do this at the aggregate expression?
C# null conditional operator ?.
would have been very handy in this case. Unfortunately it's still not supported in expression trees, so one way to achieve the goal is to build dynamically the equivalent of manual null checks all down the road:
x => x != null && x.Prop1 != null && x.Prop1.Prop2 != null ... ? (object)x.Prop1.Prop2...PropN : null
Since you would need to aggregate both the member accessor expression and condition to be used in the final Expression.Condition
, the Aggregate
method is not good - doing the aggregation with the old good foreach
loop looks more appropriate, for instance like this:
var parameter = Expression.Parameter(typeof(T));
var nullConst = Expression.Constant(null);
Expression source = parameter, condition = null;
foreach (var memberName in expressionString.Split('.'))
{
var notNull = Expression.NotEqual(source, nullConst);
condition = condition != null ? Expression.AndAlso(condition, notNull) : notNull;
source = Expression.PropertyOrField(source, memberName);
}
source = Expression.Convert(source, typeof(object));
var body = Expression.Condition(condition, source, nullConst);
return Expression.Lambda<Func<T, object>>(body, parameter)
.Compile();