dotnet/efcore · error · InvalidOperationException

Complex projections in subqueries are currently unsupported.

Error message

Complex projections in subqueries are currently unsupported.

What it means

When SelectExpression.AddJoin folds a subquery into a JOIN, it must map the inner query's projection into the outer projection. For scalar inner projections this works, but when an inner projection emits a JSON object (complex/owned value or anonymous object) there is no way to reference that object across the JOIN (tracked as TODO #34004), so it throws InvalidOperationException.

Source

Thrown at src/EFCore.Cosmos/Query/Internal/Expressions/SelectExpression.cs:599

                {
                    SqlExpression e => new ScalarReferenceExpression(joinSource.Alias, e.Type, e.TypeMapping),
                    StructuralTypeProjectionExpression e => e.Update(new ObjectReferenceExpression(e.StructuralType, joinSource.Alias)),

                    _ => throw new UnreachableException(
                        $"Unexpected expression type in projection when adding join: {expression.GetType().Name}")
                };
            }
            else
            {
                // TODO: #34004
                // The subquery is projecting out a JSON object; for the projection mapping of the outer query, we need to generate
                // property accesses over that object: Scalar/ObjectAccessExpressions over the ObjectReferenceExpression that references
                // the JOIN source.
                // However, the JSON object being projected out of the subquery doesn't correspond to any entity type, and there's currently
                // no way for us to represent a reference to that - ObjectReferenceExpression requires an IEntityType. Changing that
                // requires shaper-side changes (see comment in ObjectReferenceExpression); if we can remove that requirement, we can
                // possibly also merge ScalarReferenceExpression and ObjectReferenceExpression to a single SourceReferenceExpression.
                throw new InvalidOperationException(CosmosStrings.ComplexProjectionInSubqueryNotSupported);
            }

            projectionMapping[remappedProjectionMember] = projectionToAdd;
        }

        innerSelect.ApplyProjection();
        _sources.Add(joinSource);

        innerShaper = new ProjectionMemberRemappingExpressionVisitor(this, mapping).Visit(innerShaper);
        _projectionMapping = projectionMapping;
        innerSelect._projectionMapping.Clear();

        return New(
            transparentIdentifierType.GetTypeInfo().DeclaredConstructors.Single(),
            [outerShaper, innerShaper], outerMemberInfo, innerMemberInfo);
    }

    /// <summary>

View on GitHub (pinned to dbf9771522)

Solutions

  1. Project scalar properties out of the inner subquery instead of whole objects (e.g. Select(x => x.Name) rather than selecting the entity).
  2. Restructure the query to avoid the join (fetch roots then load related data separately).
  3. Materialize the relevant entities client-side with separate queries and stitch them in memory.

Example fix

// before
var q = from o in ctx.Orders
        from c in ctx.Customers.Where(c => c.Id == o.CustomerId).Select(c => c.Address)
        select new { o, c };

// after: project scalars from the inner subquery
var q = from o in ctx.Orders
        from c in ctx.Customers.Where(c => c.Id == o.CustomerId)
        select new { o.Id, City = c.Address.City };
Defensive patterns

Strategy: validation

Validate before calling

// Project scalars from the inner side of joins to avoid complex subquery projections.
static IQueryable<TResult> JoinScalar<TOuter, TInner, TKey, TResult>(
    IQueryable<TOuter> outer, IQueryable<TInner> inner,
    Expression<Func<TOuter, TKey>> ok, Expression<Func<TInner, TKey>> ik,
    Expression<Func<TOuter, TInner, TResult>> sel)
    where TResult : class // ensure TResult is built from scalars, not whole objects
    => outer.Join(inner, ok, ik, sel);

Try / catch

try { var q = query.ToList(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Complex projections in subqueries"))
{ /* project scalars or load client-side */ }

Prevention

When it happens

Trigger: A LINQ query whose join/select-many inner source projects a complex or owned object (non-scalar) rather than scalar fields — e.g. joining and then projecting whole embedded entities from the inner side.

Common situations: Complex projections inside joins; projecting owned types out of a joined subquery; advanced SelectMany shapes ported from relational providers.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/48a387676a65bb85. Report an issue: GitHub.