0Day Forums
An expression tree lambda may not contain a null propagating operator - Printable Version

+- 0Day Forums (https://zeroday.vip)
+-- Forum: Coding (https://zeroday.vip/Forum-Coding)
+--- Forum: C# (https://zeroday.vip/Forum-C)
+--- Thread: An expression tree lambda may not contain a null propagating operator (/Thread-An-expression-tree-lambda-may-not-contain-a-null-propagating-operator)



An expression tree lambda may not contain a null propagating operator - cowsheds962191 - 07-24-2023

The line `price = co?.price ?? 0,` in the following code gives me the above error, but if I remove `?` from `co.?` it works fine.

I was trying to follow [this MSDN example][1] where they are using `?` on line `select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };` So, it seems I need to understand when to use `?` with `??` and when not to.

**Error**:

>an expression tree lambda may not contain a null propagating operator

public class CustomerOrdersModelView
{
public string CustomerID { get; set; }
public int FY { get; set; }
public float? price { get; set; }
....
....
}
public async Task<IActionResult> ProductAnnualReport(string rpt)
{
var qry = from c in _context.Customers
join ord in _context.Orders
on c.CustomerID equals ord.CustomerID into co
from m in co.DefaultIfEmpty()
select new CustomerOrdersModelView
{
CustomerID = c.CustomerID,
FY = c.FY,
price = co?.price ?? 0,
....
....
};
....
....
}


[1]:

[To see links please register here]




RE: An expression tree lambda may not contain a null propagating operator - otheliag - 07-24-2023

The example you were quoting from uses LINQ to Objects, where the implicit lambda expressions in the query are converted into *delegates*... whereas you're using EF or similar, with `IQueryable<T>` queryies, where the lambda expressions are converted into *expression trees*. Expression trees don't support the null conditional operator (or tuples).

Just do it the old way:

price = co == null ? 0 : (co.price ?? 0)

(I believe the null-coalescing operator is fine in an expression tree.)


RE: An expression tree lambda may not contain a null propagating operator - aspring487743 - 07-24-2023

The code you link to uses `List<T>`. `List<T>` implements `IEnumerable<T>` but not `IQueryable<T>`. In that case, the projection is executed in memory and `?.` works.

You're using some `IQueryable<T>`, which works very differently. For `IQueryable<T>`, a representation of the projection is created, and your LINQ provider decides what to do with it at runtime. For backwards compatibility reasons, `?.` cannot be used here.

Depending on your LINQ provider, you may be able to use plain `.` and still not get any `NullReferenceException`.


RE: An expression tree lambda may not contain a null propagating operator - waterloggedness976700 - 07-24-2023

Jon Skeet's answer was right, in my case I was using `DateTime` for my Entity class.
When I tried to use like
```
(a.DateProperty == null ? default : a.DateProperty.Date)
```
I had the error
```
Property 'System.DateTime Date' is not defined for type 'System.Nullable`1[System.DateTime]' (Parameter 'property')
```

So I needed to change `DateTime?` for my entity class and
```
(a.DateProperty == null ? default : a.DateProperty.Value.Date)
```


RE: An expression tree lambda may not contain a null propagating operator - behzadug - 07-24-2023

While expression tree does not support the C# 6.0 null propagating, what we can do is create a visitor that modify expression tree for safe null propagation, just like the operator does!

[Here is mine:]()

```c#
public class NullPropagationVisitor : ExpressionVisitor
{
private readonly bool _recursive;

public NullPropagationVisitor(bool recursive)
{
_recursive = recursive;
}

protected override Expression VisitUnary(UnaryExpression propertyAccess)
{
if (propertyAccess.Operand is MemberExpression mem)
return VisitMember(mem);

if (propertyAccess.Operand is MethodCallExpression met)
return VisitMethodCall(met);

if (propertyAccess.Operand is ConditionalExpression cond)
return Expression.Condition(
test: cond.Test,
ifTrue: MakeNullable(Visit(cond.IfTrue)),
ifFalse: MakeNullable(Visit(cond.IfFalse)));

return base.VisitUnary(propertyAccess);
}

protected override Expression VisitMember(MemberExpression propertyAccess)
{
return Common(propertyAccess.Expression, propertyAccess);
}

protected override Expression VisitMethodCall(MethodCallExpression propertyAccess)
{
if (propertyAccess.Object == null)
return base.VisitMethodCall(propertyAccess);

return Common(propertyAccess.Object, propertyAccess);
}

private BlockExpression Common(Expression instance, Expression propertyAccess)
{
var safe = _recursive ? base.Visit(instance) : instance;
var caller = Expression.Variable(safe.Type, "caller");
var assign = Expression.Assign(caller, safe);
var acess = MakeNullable(new ExpressionReplacer(instance,
IsNullableStruct(instance) ? caller : RemoveNullable(caller)).Visit(propertyAccess));
var ternary = Expression.Condition(
test: Expression.Equal(caller, Expression.Constant(null)),
ifTrue: Expression.Constant(null, acess.Type),
ifFalse: acess);

return Expression.Block(
type: acess.Type,
variables: new[]
{
caller,
},
expressions: new Expression[]
{
assign,
ternary,
});
}

private static Expression MakeNullable(Expression ex)
{
if (IsNullable(ex))
return ex;

return Expression.Convert(ex, typeof(Nullable<>).MakeGenericType(ex.Type));
}

private static bool IsNullable(Expression ex)
{
return !ex.Type.IsValueType || (Nullable.GetUnderlyingType(ex.Type) != null);
}

private static bool IsNullableStruct(Expression ex)
{
return ex.Type.IsValueType && (Nullable.GetUnderlyingType(ex.Type) != null);
}

private static Expression RemoveNullable(Expression ex)
{
if (IsNullableStruct(ex))
return Expression.Convert(ex, ex.Type.GenericTypeArguments[0]);

return ex;
}

private class ExpressionReplacer : ExpressionVisitor
{
private readonly Expression _oldEx;
private readonly Expression _newEx;

internal ExpressionReplacer(Expression oldEx, Expression newEx)
{
_oldEx = oldEx;
_newEx = newEx;
}

public override Expression Visit(Expression node)
{
if (node == _oldEx)
return _newEx;

return base.Visit(node);
}
}
}
```

It passes on the following tests:

```c#
private static string Foo(string s) => s;

static void Main(string[] _)
{
var visitor = new NullPropagationVisitor(recursive: true);

Test1();
Test2();
Test3();

void Test1()
{
Expression<Func<string, char?>> f = s => s == "foo" ? 'X' : Foo(s).Length.ToString()[0];

var fBody = (Expression<Func<string, char?>>)visitor.Visit(f);

var fFunc = fBody.Compile();

Debug.Assert(fFunc(null) == null);
Debug.Assert(fFunc("bar") == '3');
Debug.Assert(fFunc("foo") == 'X');
}

void Test2()
{
Expression<Func<string, int>> y = s => s.Length;

var yBody = visitor.Visit(y.Body);
var yFunc = Expression.Lambda<Func<string, int?>>(
body: yBody,
parameters: y.Parameters)
.Compile();

Debug.Assert(yFunc(null) == null);
Debug.Assert(yFunc("bar") == 3);
}

void Test3()
{
Expression<Func<char?, string>> y = s => s.Value.ToString()[0].ToString();

var yBody = visitor.Visit(y.Body);
var yFunc = Expression.Lambda<Func<char?, string>>(
body: yBody,
parameters: y.Parameters)
.Compile();

Debug.Assert(yFunc(null) == null);
Debug.Assert(yFunc('A') == "A");
}
}
```