{"record":{"id":"50e7f181d3b07b3c","repo":"dotnet/efcore","slug":"lambda-with-null-expression-body","errorCode":null,"errorMessage":"Lambda with null expression body","messagePattern":"Lambda with null expression body","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Design/Query/Internal/CSharpToLinqTranslator.cs","lineNumber":1032,"sourceCode":"\n        var type = ResolveType(typeSymbol);\n        return Constant(type, typeof(Type));\n    }\n\n    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    public override Expression DefaultVisit(SyntaxNode node)\n        => throw new NotSupportedException($\"Unsupported syntax node of type '{node.GetType()}': {node}\");\n\n    private Expression VisitLambdaExpression(AnonymousFunctionExpressionSyntax lambda, Type? expectedType = null)\n    {\n        if (lambda.ExpressionBody is null)\n        {\n            throw new NotSupportedException(\"Lambda with null expression body\");\n        }\n\n        if (lambda.Modifiers.Any())\n        {\n            throw new NotSupportedException(\"Lambda with modifiers not supported: \" + lambda.Modifiers);\n        }\n\n        if (!lambda.AsyncKeyword.IsKind(SyntaxKind.None))\n        {\n            throw new NotSupportedException(\"Async lambdas are not supported\");\n        }\n\n        var lambdaParameters = lambda switch\n        {\n            SimpleLambdaExpressionSyntax simpleLambda => SyntaxFactory.SingletonSeparatedList(simpleLambda.Parameter),\n            ParenthesizedLambdaExpressionSyntax parenthesizedLambda => parenthesizedLambda.ParameterList.Parameters,\n\n            _ => throw new UnreachableException()","sourceCodeStart":1014,"sourceCodeEnd":1050,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Design/Query/Internal/CSharpToLinqTranslator.cs#L1014-L1050","documentation":"Thrown as NotSupportedException by VisitLambdaExpression when a lambda expression has a null ExpressionBody — meaning it is a statement lambda (a lambda with a block body containing statements) rather than an expression lambda (a single expression body). The translator can only convert expression-bodied lambdas into LINQ expression-tree LambdaExpressions. Statement lambdas would require full statement-to-expression-tree translation, which is not supported.","triggerScenarios":"A precompiled query contains a lambda with a statement block body instead of a single expression: `ctx.Blogs.Where(b => { var x = b.Id + 1; return x > 10; })`. The translator's VisitLambdaExpression checks `lambda.ExpressionBody is null` (which is true for block-bodied lambdas) and throws NotSupportedException.","commonSituations":"Developers who use statement lambdas for complex logic inside LINQ calls; refactoring expression lambdas into statement lambdas during debugging (adding logging, breakpoints); multi-line predicates that were written as blocks for readability; code produced by refactoring tools that convert expressions to statement blocks; developers unfamiliar with the expression-tree limitation on statement lambdas.","solutions":["Rewrite the statement lambda as an expression lambda: extract any local variables into the expression using let-style chaining or inline them, and use ternary operators instead of if statements.","Move complex logic that requires statements into a separate helper method and call it from the expression lambda (ensure the method is translatable).","Replace if-else logic with conditional (ternary) expressions: `b => b.Id > 0 ? b.Name : null`.","Replace local variable computations with method chaining: instead of `b => { var x = b.Salary * 2; return x > 1000; }` use `b => b.Salary * 2 > 1000`.","Remove any debugging code (Console.WriteLine, logging) from lambdas inside precompiled queries."],"exampleFix":"// before — statement lambda\nvar query = ctx.Blogs.Where(b =>\n{\n    var adjusted = b.Score * b.Weight;\n    return adjusted > 100;\n});\n\n// after — expression lambda\nvar query = ctx.Blogs.Where(b => b.Score * b.Weight > 100);","handlingStrategy":"validation","validationCode":"// Before precompiling, scan for statement lambdas (block-bodied lambdas)\nvar statementLambdas = querySyntaxTree.DescendantNodes()\n    .Where(n => n is SimpleLambdaExpressionSyntax or ParenthesizedLambdaExpressionSyntax)\n    .Cast<AnonymousFunctionExpressionSyntax>()\n    .Where(l => l.ExpressionBody is null); // null ExpressionBody means block body\nif (statementLambdas.Any())\n    ReportError(\"Statement lambdas (block bodies) are not supported in precompiled queries — use expression lambdas\");","typeGuard":"// Type guard to distinguish expression lambdas from statement lambdas (Roslyn CSharpSyntaxVisitor level)\n// ExpressionBody is non-null for expression lambdas, null for statement lambdas\nstatic bool IsExpressionLambda(AnonymousFunctionExpressionSyntax lambda)\n    => lambda.ExpressionBody is not null;\n\n// Usage before precompilation:\n// if (!IsExpressionLambda(lambdaNode)) skipPrecompilation(lambdaNode);","tryCatchPattern":null,"preventionTips":["Always use expression-bodied lambdas (single expression, no braces) in precompiled queries.","Replace if-else logic inside lambdas with ternary operators (?:).","Inline local variables directly into the expression or compute them before the query.","Remove debugging/logging statements from precompiled query lambdas.","Extract multi-step logic into separate helper methods called from the expression lambda.","Use the IDE to collapse statement lambdas into expression lambdas before enabling precompilation."],"tags":["precompiled-queries","roslyn","lambda","statement-lambda","expression-tree","not-supported"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}