-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Description
When Select projects into an anonymous type, the resulting IRewritableQueryable<AnonymousType> has an anonymous element type. Subsequent delegate-based operators (e.g., OrderByDescending, Where) are not intercepted — the stub throws UnreachableException at runtime.
Reproduction
db.Orders
.WithExpressionRewrite()
.Where(o => o.Customer?.Name != null)
.Select(o => new { o.Id, o.Total, o.Grade })
.OrderByDescending(o => o.Total) // throws UnreachableException
.ToListAsync();Error: UnreachableException: This method must be intercepted by the ExpressiveSharp source generator.
Root Cause
PolyfillInterceptorGenerator.TryEmit has an early guard at line ~290:
if (elementSymbol.IsAnonymousType)
return null; // Anonymous element types can't be named in the interceptor bodyThis guard exists because the concrete (non-generic) interceptor code path can't name anonymous types. However, the generic code path (with T0, T1 type params) could handle this — it's already used for anonymous types in Select and other projection methods.
Workaround
Use a named type for the projection:
record OrderSummary(int Id, double Total, string Grade);
db.Orders.WithExpressionRewrite()
.Where(o => o.Customer?.Name != null)
.Select(o => new OrderSummary(o.Id, o.Total, o.Grade))
.OrderByDescending(o => o.Total) // works
.ToListAsync();Fix
Remove or relax the IsAnonymousType guard for element types. When the element type is anonymous, route all operators through the generic (anonymous-aware) code path that uses type parameters instead of concrete type names. This affects both the dedicated emitters and EmitGenericSingleLambda.
Found via stress test in samples/EFCoreSample/StressTest.cs.