Query translation fails when `Where` references a projection guarded by `child != null && grandChild != null`
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
### Bug description
A query that projects an object conditionally using `child != null && grandChild != null ? ... : null` executes successfully on its own, but fails to translate once a `Where` clause references a property of the projected object.
The equivalent logic expressed using nested ternary operators (`child != null ? grandChild != null ? ... : null : null`) translates successfully, even though it is semantically equivalent.
This suggests that EF Core generates a different expression tree for the two forms and does not correctly translate the `&&` version when the projection is later referenced in a predicate.
## Expected behavior
Both forms are semantically equivalent and should either translate successfully or fail consistently. The query using the `&&` conditional should translate the same way as the nested ternary version.
## Actual behavior
The query fails during translation with:
```
System.InvalidOperationException: The LINQ expression ... could not be translated.
```
## Additional information
* The initial projection (`ToListAsync()`) succeeds.
* Translation only fails when a subsequent query operator references the projected conditional object.
* Rewriting the condition as nested ternary operators is a viable workaround, indicating this is likely a query translation issue rather than an unsupported LINQ pattern.
### Your code
```csharp
// Entities
public class Parent
{
public Guid Id { get; set; }
public Guid? ChildId { get; set; }
}
public class Child
{
public Guid Id { get; set; }
public Guid? GrandChildId { get; set; }
}
public class GrandChild
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
}
// Reproduce the bug
var query = from parent in context.Parents
join child in context.Children on parent.ChildId equals child.Id into childGroup
from child in childGroup.DefaultIfEmpty()
join grandChild in context.GrandChildren on child.GrandChildId equals grandChild.Id into grandChildGroup
from grandChild in grandChildGroup.DefaultIfEmpty()
select new
{
Id = parent.Id,
Child = child != null && grandChild != null ? new
{
GrandChildId = child.GrandChildId
} : null,
};
// ✅ This works
var result = await query.ToListAsync();
// ❌ This throws InvalidOperationException - cannot be translated
var result2 = await query.Where(x => x.Child.GrandChildId != null).ToListAsync();
// Workaround - Nested ternary operators
var workingQuery = from parent in context.Parents
join child in context.Children on parent.ChildId equals child.Id into childGroup
from child in childGroup.DefaultIfEmpty()
join grandChild in context.GrandChildren on child.GrandChildId equals grandChild.Id into grandChildGroup
from grandChild in grandChildGroup.DefaultIfEmpty()
select new
{
Id = parent.Id,
// Workaround: nest the ternary operators
Child = child != null ? grandChild != null ? new
{
GrandChildId = child.GrandChildId
} : null : null,
};
// ✅ Workaround using nested ternary operator works
var workingResult = await workingQuery.ToListAsync();
```
### Stack traces
```text
System.InvalidOperationException: The LINQ expression 'DbSet()
.LeftJoin(
inner: DbSet(),
outerKeySelector: p => (object)p.ChildId,
innerKeySelector: c => (object)(Guid?)c.Id,
resultSelector: (p, c) => new TransparentIdentifier(
Outer = p,
Inner = c
))
.LeftJoin(
inner: DbSet(),
outerKeySelector: ti => (object)ti.Inner.GrandChildId,
innerKeySelector: g => (object)(Guid?)g.Id,
resultSelector: (ti, g) => new TransparentIdentifier, GrandChild>(
Outer = ti,
Inner = g
))
.Where(ti0 => ti0.Outer.Inner != null && ti0.Inner != null ? new Child{ GrandChildId = ti0.Outer.Inner.GrandChildId }
: null.GrandChildId != null)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.Translate(Expression expression)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutorExpression[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass11_0`1.b__0()
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteCore[TResult](Expression query, Boolean async, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
``
```
### Verbose output
```text
```
### EF Core version
10.0.9
### Database provider
Microsoft.EntityFrameworkCore.SqlServer
### Target framework
.NET 10
### Operating system
Windows 11
### IDE
Visual Studio 2026 18.7.3
Contributor guide
Assessment
This issue has not been assessed yet.