Skip to content

EmitJoin does not handle anonymous types in result selector #8

@koenbeuk

Description

@koenbeuk

Description

When using Join with an anonymous type result selector on IRewritableQueryable<T>, the generated interceptor code contains invalid type references and fails to compile.

Reproduction

db.Orders
    .WithExpressionRewrite()
    .Join(db.Customers,
          o => o.CustomerId,
          c => c.Id,
          (o, c) => new { o.Id, CustomerName = c.Name })  // anonymous result
    .ToListAsync();

Error: Generated code references anonymous type directly instead of using generic type parameters (TResult).

Root Cause

EmitJoin only has a concrete-type code path. It doesn't have the anonymous-type handling branch that EmitSelectMany3 has (lines 619-653) — where anonymous types are mapped to generic type parameters via typeAliases and the method signature uses <TElem, TInner, TKey, TResult>.

Workaround

Use a named type (record/class/struct) instead of an anonymous type:

record OrderCustomer(int Id, string CustomerName);

db.Orders.WithExpressionRewrite()
    .Join(db.Customers, o => o.CustomerId, c => c.Id,
          (o, c) => new OrderCustomer(o.Id, c.Name))
    .ToListAsync();

Fix

Add an anonymous-type branch to EmitJoin following the EmitSelectMany3 pattern: detect anonymous types in typeArgs, create typeAliases, generate generic type parameters, and emit (object) casts in the body.

Found via stress test in samples/EFCoreSample/StressTest.cs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions