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"); } } ``` |