Repository navigation
Relational query translation fails when a nested OwnsMany collection is projected to a DTO and then re-projected to the same DTO shape #39415
Description
Bug description
EF Core can translate a query that projects an owned collection navigation to a nested DTO collection. It fails when a later LINQ component composes a second projection that reconstructs the same DTO shape.
This composition is needed by query-shaping frameworks such as GraphQL projection middleware (in our case, part of the Hot Chocolate framework):
- The initial DTO projection maps an entity model to an application DTO.
- The later projection selects only the fields requested by the client.
The failure occurs during query compilation, before SQL is executed. The exception identifies the second collection selector as an untranslatable Func<NestedItemDto, NestedItemDto>.
Expected behavior
The provider should be able to flatten the second DTO projection into the initial projection and generate SQL equivalent to selecting the requested owned collection columns from the NestedItems table.
Actual behavior
Running the query (or calling ToQueryString()) for the composed query throws InvalidOperationException:
The LINQ expression 'p2 => new NestedItemDto{
Name = p2.Name,
Amount = p2.Amount
}
' 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'.
The original projection, before the second DTO-to-DTO projection is composed, translates successfully.
Your code
using Microsoft.EntityFrameworkCore;
using var db = new ReproDbContext();
var initialProjection = db.RootEntities.Select(entity => new RootDto
{
Id = entity.Id,
NestedComponent = entity.NestedComponent == null
? null
: new NestedComponentDto
{
Name = entity.NestedComponent.Name,
NestedItems = entity.NestedComponent.NestedItems
.Select(item => new NestedItemDto
{
Name = item.Name,
Amount = item.Amount
})
.ToList()
}
});
// Succeeds: the entity-to-DTO projection is directly translated.
Console.WriteLine(initialProjection.ToQueryString());
var composedProjection = initialProjection.Select(entity => new RootDto
{
Id = entity.Id,
NestedComponent = entity.NestedComponent == null
? null
: new NestedComponentDto
{
Name = entity.NestedComponent.Name,
NestedItems = entity.NestedComponent.NestedItems
.Select(item => new NestedItemDto
{
Name = item.Name,
Amount = item.Amount
})
.ToList()
}
});
// Throws InvalidOperationException while EF Core translates the nested second projection.
Console.WriteLine(composedProjection.ToQueryString());
public sealed class ReproDbContext : DbContext
{
public DbSet<RootEntity> RootEntities => Set<RootEntity>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
optionsBuilder.UseSqlServer(
"Server=(localdb)\\mssqllocaldb;Database=EfCoreDtoReprojectionRepro;Trusted_Connection=True;TrustServerCertificate=True");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<RootEntity>(entity =>
{
entity.HasKey(rootEntity => rootEntity.Id);
entity.OwnsOne(rootEntity => rootEntity.NestedComponent, component =>
{
component.OwnsMany(nestedComponent => nestedComponent.NestedItems, items =>
{
items.ToTable("NestedItems");
});
});
});
}
}
public sealed class RootEntity
{
public long Id { get; set; }
public NestedComponent? NestedComponent { get; set; }
}
public sealed class NestedComponent
{
public string Name { get; set; } = null!;
public List<NestedItem> NestedItems { get; set; } = [];
}
public sealed class NestedItem
{
public string Name { get; set; } = null!;
public int Amount { get; set; }
}
public sealed class RootDto
{
public long Id { get; init; }
public NestedComponentDto? NestedComponent { get; init; }
}
public sealed class NestedComponentDto
{
public string Name { get; init; } = null!;
public List<NestedItemDto> NestedItems { get; init; } = [];
}
public sealed class NestedItemDto
{
public string Name { get; init; } = null!;
public int Amount { get; init; }
}Stack traces
System.InvalidOperationException: The LINQ expression 'item => new NestedItemDto{
Name = item.Name,
Amount = item.Amount
}
' 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.RelationalSqlTranslatingExpressionVisitor.VisitLambda[T](Expression`1 lambdaExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.TranslateInternal(Expression expression, Boolean applyDefaultTypeMapping)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.TranslateProjection(Expression expression, Boolean applyDefaultTypeMapping)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitConditional(ConditionalExpression conditionalExpression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Translate(SelectExpression selectExpression, Expression expression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
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.<ExecuteCore>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.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToQueryString(IQueryable source)
Verbose output
EF Core version
10.0.13
Database provider
Microsoft.EntityFrameworkCore.SqlServer 10.0.13
Target framework
.NET 10
Operating system
No response
IDE
No response
Activity
MihuBot commentedon Oct 26, 2026
I'm a bot. Here is a possible related and/or duplicate issue (I may be wrong):
fschmied commentedon Oct 26, 2026
Interesting, I didn't see that when I searched. That actually looks like the same problem. I'll close this issue.