If an entity contains read-write properties and there are one to many sub entities, including sub entities in the query will result in the generated query having many join clauses. The number of join clauses is the same as the number of properties of the entity.
Minimum reproducible code.
Entities
public class Entity1
{
public virtual int Id { get; set; }
public virtual string? Name1 { get; set; }
[NotMapped]
[Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore, UseMemberBody = nameof(Name1LenQuery))]
public virtual int? Name1Len { get; set; }
protected virtual int? Name1LenQuery => Name1?.Length;
public virtual List<Entity2> Entity2s { get; set; } = [];
}
public class Entity2
{
public virtual int Id { get; set; }
public virtual string? Name2 { get; set; }
public virtual int? Entity1Id { get; set; }
public virtual Entity1? Entity1 { get; set; }
}
The projectable property Name1Len and navigation Entity2s are the key part that causes the problem.
DbContext
public class DemoDbContext(DbContextOptions<DemoDbContext> options) : DbContext(options)
{
public DbSet<Entity1> Entity1s => Set<Entity1>();
public DbSet<Entity2> Entity2s => Set<Entity2>();
}
QueryExpressionMonitor(for breakpoint to view final expression)
public class MonitingExpressionInterceptor : IQueryExpressionInterceptor
{
public Expression QueryCompilationStarting(Expression queryExpression, QueryExpressionEventData eventData)
{
return queryExpression;
}
}
Program.Main
using var context = new DemoDbContext(new DbContextOptionsBuilder<DemoDbContext>()
.UseSqlite("Data Source=test.db")
.UseProjectables()
.AddInterceptors(new MonitingExpressionInterceptor())
.Options);
var q = context.Entity1s
.AsNoTrackingWithIdentityResolution()
.Include(e1 => e1.Entity2s);
var qs = q.ToQueryString();
;
Final expression(output from VS 2026 plugin: ReadableExpressions v4.8.0)
[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression]
.AsNoTrackingWithIdentityResolution()
.Include((Entity1 e1) => e1.Entity2s)
.Select(
(Entity1 entity1) => new Entity1
{
k__BackingField = entity1.k__BackingField,
k__BackingField = entity1.k__BackingField,
k__BackingField = entity1.k__BackingField,
Name1Len = (int?)entity1.Name1.Length
})
Entity1's Properties Id, Name1 and Entity2s has been rewritten as field.
SQL
SELECT "e"."Id", "e"."Name1", "e0"."Id", "e0"."Entity1Id", "e0"."Name2", "e1"."Id", "e1"."Entity1Id", "e1"."Name2", "e2"."Id", "e2"."Entity1Id", "e2"."Name2", length("e"."Name1")
FROM "Entity1s" AS "e"
LEFT JOIN "Entity2s" AS "e0" ON "e"."Id" = "e0"."Entity1Id"
LEFT JOIN "Entity2s" AS "e1" ON "e"."Id" = "e1"."Entity1Id"
LEFT JOIN "Entity2s" AS "e2" ON "e"."Id" = "e2"."Entity1Id"
ORDER BY "e"."Id", "e0"."Id", "e1"."Id"
The number of join clauses in the generated SQL is the same as the number of fields being rewritten.
Version info
Framework: net10.0
EntityFrameworkCore.Projectables: 6.0.5
Microsoft.EntityFrameworkCore.Sqlite: 10.0.9
If an entity contains read-write properties and there are one to many sub entities, including sub entities in the query will result in the generated query having many join clauses. The number of join clauses is the same as the number of properties of the entity.
Minimum reproducible code.
Entities
The projectable property Name1Len and navigation Entity2s are the key part that causes the problem.
DbContext
QueryExpressionMonitor(for breakpoint to view final expression)
Program.Main
Final expression(output from VS 2026 plugin: ReadableExpressions v4.8.0)
Entity1's Properties Id, Name1 and Entity2s has been rewritten as field.
SQL
The number of join clauses in the generated SQL is the same as the number of fields being rewritten.
Version info
Framework: net10.0
EntityFrameworkCore.Projectables: 6.0.5
Microsoft.EntityFrameworkCore.Sqlite: 10.0.9