dotnet/efcore · error · InvalidOperationException

Cannot translate '{comparisonOperator}' on a subquery expres

Error message

Cannot translate '{comparisonOperator}' on a subquery expression of entity type '{entityType}' because it has a composite primary key. See https://go.microsoft.com/fwlink/?linkid=2141942 for information on how to rewrite your query.

What it means

Thrown by TryRewriteContainsEntity when a Contains operates on a subquery whose entity type has a composite primary key (more than one key property). The Cosmos provider's Contains rewrite extracts a single key property into a list; with a composite key it cannot produce a valid single-value Contains, so it throws.

Source

Thrown at src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.StructuralEquality.cs:42

    {
        result = null;

        if (item is not StructuralTypeReferenceExpression { StructuralType: IEntityType entityType })
        {
            return false;
        }

        var primaryKeyProperties = entityType.FindPrimaryKey()?.Properties;

        switch (primaryKeyProperties)
        {
            case null:
                throw new InvalidOperationException(
                    CoreStrings.EntityEqualityOnKeylessEntityNotSupported(
                        nameof(Queryable.Contains), entityType.DisplayName()));

            case { Count: > 1 }:
                throw new InvalidOperationException(
                    CoreStrings.EntityEqualityOnCompositeKeyEntitySubqueryNotSupported(
                        nameof(Queryable.Contains), entityType.DisplayName()));
        }

        var property = primaryKeyProperties[0];
        Expression rewrittenSource;
        switch (source)
        {
            case SqlConstantExpression sqlConstantExpression:
                var values = (IEnumerable)sqlConstantExpression.Value!;
                var propertyValueList =
                    (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(property.ClrType.MakeNullable()))!;
                var propertyGetter = property.GetGetter();
                foreach (var value in values)
                {
                    propertyValueList.Add(propertyGetter.GetClrValue(value));
                }

View on GitHub (pinned to dbf9771522)

Solutions

  1. Rewrite the Contains as an Any with a predicate comparing all composite key properties: subquery.Any(x => x.K1 == e.K1 && x.K2 == e.K2).
  2. Project a single discriminant value (e.g. a concatenated id) from the subquery and Contains on that.
  3. Materialize the subquery results client-side and compare composite keys in memory.

Example fix

// before
var exists = await context.Items.Where(...).Contains(item).AnyAsync();
// after
var exists = await context.Items.Where(...)
    .AnyAsync(x => x.K1 == item.K1 && x.K2 == item.K2);
Defensive patterns

Strategy: validation

Validate before calling

// For composite keys, use Any with a multi-property predicate
var exists = await subquery.AnyAsync(x => x.K1 == item.K1 && x.K2 == item.K2);

Prevention

When it happens

Trigger: Calling Contains on the result of a subquery returning entities with composite keys, e.g. subquery.Where(...).Contains(entity) where the entity has multiple PK properties.

Common situations: Entities modeled with composite keys used in Contains against a subquery. Relational patterns ported to Cosmos where composite-key Contains was supported differently.

Related errors


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