3

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?

Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
nilphilus
  • 590
  • 1
  • 5
  • 23
  • The `?.` is syntactic sugar, you should build the expression step by step with `Expression.IfThenElse()` – Jeroen van Langen Jul 11 '19 at 08:03
  • @JeroenvanLangen Expression.Condition will not be better then? – nilphilus Jul 11 '19 at 12:18
  • Yes, That would be better, i guess – Jeroen van Langen Jul 11 '19 at 12:59
  • 1
    Take a look at [this](https://stackoverflow.com/a/39617419/4685428) answer – Aleks Andreev Jul 11 '19 at 13:18
  • 3
    Since `?.` still has no natural support in expression trees, and if you want to keep it compatible with query providers (i.e. don't use block expressions), there is no other way that simulating it via (nested) `Expression.Condition`. But note that the query providers which translate such expressions naturally support null propagation on `.` ( there are no "objects" in sql, hence no NRE), so in such case you don't need to do anything. – Ivan Stoev Jul 12 '19 at 08:38

1 Answers1

2

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)
aperezfals
  • 1,341
  • 1
  • 10
  • 26
  • 4
    Unfortunately, this implementation is _not_ actually equivalent to the null-conditional operator. The actual C# operator will retrieve the possibly-null expression _once_, and then use that throughout. This makes it, for example, perfectly suited for raising C# events in a thread-safe way. IMHO it is erroneous and dangerous to provide a proposed implementation for the operator that doesn't have the same behavior as the actual operator. – Peter Duniho Jul 18 '19 at 17:27