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 from TryRewriteContainsEntity when Contains is applied to a subquery whose entity type has a composite primary key (more than one key property). The InMemory translator can only rewrite Contains over single-key entities; composite-key entity subqueries have no single column to expand into an IN list.

Source

Thrown at src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs:1275

    {
        result = null;

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

        var primaryKeyProperties = entityType.FindPrimaryKey()?.Properties;
        if (primaryKeyProperties == null)
        {
            throw new InvalidOperationException(
                CoreStrings.EntityEqualityOnKeylessEntityNotSupported(
                    nameof(Queryable.Contains), entityType.DisplayName()));
        }

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

        var property = primaryKeyProperties[0];
        Expression rewrittenSource;
        switch (source)
        {
            case ConstantExpression constantExpression:
                var values = constantExpression.GetConstantValue<IEnumerable>();
                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 3a2006ef56)

Solutions

  1. Rewrite Contains on a composite-key entity as a JOIN on all key columns, or as multiple paired Where conditions.
  2. Materialize the subquery with AsEnumerable() and perform the Contains client-side.
  3. Project to a tuple/string key and Contains on that single value: db.Orders.Select(o => new { o.A, o.B }).AsEnumerable().Contains(...).
  4. Avoid Contains over composite-key subqueries; prefer Any(x => x.A == a && x.B == b).

Example fix

// before
db.Joins.Contains(joinEntity);
// after
db.Joins.Any(j => j.A == joinEntity.A && j.B == joinEntity.B);
Defensive patterns

Strategy: validation

Validate before calling

var pk = dbContext.Model.FindEntityType(typeof(Join))?.FindPrimaryKey();
if (pk is { Properties.Count: > 1 })
    throw new InvalidOperationException("Composite-key entity: use Any(...) instead of Contains over a subquery");

Type guard

static bool HasCompositeKey(IEntityType et)
    => et.FindPrimaryKey()?.Properties.Count > 1;

Try / catch

try { return db.Joins.Contains(item); }
catch (InvalidOperationException ex) when (ex.Message.Contains("composite primary key"))
{
    return db.Joins.Any(j => j.A == item.A && j.B == item.B);
}

Prevention

When it happens

Trigger: Queryable.Contains over a subquery (e.g. db.Orders.Contains(order) where db.Orders is itself a subquery, or the source is a ConstantExpression of entities) and the entity's FindPrimaryKey().Properties.Count > 1.

Common situations: Entities with composite keys (junction tables, many-to-many join entities) used inside a Contains against a subquery. Refactoring equality checks to use Contains when moving to composite keys.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/3662e9fddd4881a1. Report an issue: GitHub.