Search code examples
c#expression-treessystem.reflection

Expression Tree C# - Null-Conditional oeprator (?.)


I'm trying to build in Expression Trees equivalent of "?." operator.

var member = Expression.Property(Expression.Property("PropertyObjectName", value.Property), 
    "PropertyOfObject");

which is: member.PropertyObjectName.PropertyOfObject which of course will throw Null exception if PropertyObjectName is null which I want to avoid.

Is there any way to build member?.PropertyObjectName?.PropertyOfObject other then putting there ConditionalExpression?


Solution

  • You can use my Extension method

    public static class ExpressionExtensions
        {
            public static Expression ElvisOperator(this Expression expression, string propertyOrField)
            {
                return Expression.Condition(Expression.NotEqual(expression, Expression.Constant(null)),
                    Expression.PropertyOrField(expression, propertyOrField),
                    Expression.Constant(null, expression.Type)
                    );
            }
        }
    

    this generates something like

    IIF((x != null), x.propertyname, null)
    

    Then you can use it like this:

    public class TestClass
        {
            public int FirstProp { get; set; }
    
            public TestClass SecondProp { get; set; } 
        }
    
    
    var variable = Expression.Parameter(typeof(TestClass), "x");
    var nullSafe = variable.ElvisOperator("SecondProp");
    

    I made this test:

    List<TestClass> tests = new List<TestClass>(){
                new TestClass() { FirstProp = 1, SecondProp = new TestClass() { SecondProp = new TestClass() } },
                new TestClass() { FirstProp = 2 },
                new TestClass() { FirstProp = 3, SecondProp = new TestClass() },
                new TestClass() { FirstProp = 4 },
            };
    
            var variable = Expression.Parameter(typeof(TestClass), "x");
            var nullSafe = variable.ElvisOperator("SecondProp").ElvisOperator("SecondProp");
            var cond = Expression.NotEqual(nullSafe, Expression.Constant(null, variable.Type));
    
            var lambda = Expression.Lambda<Func<TestClass, bool>>(cond, new ParameterExpression[] { variable });
    
            tests = tests.AsQueryable().Where(lambda).ToList();
    
            Console.WriteLine(tests.Count);
    

    This prints 1 on the console because filters the list like this:

    .Where(x => x.SecondProp?SecondProp != null)