dotnet/efcore · error · InvalidOperationException

Cannot translate ' ' on a subquery expression of entity…

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

For Contains on a subquery of entities, StructuralEquality can only expand to an IN against a single key property. When the primary key has more than one property (composite key) there is no single column to compare, so it throws CoreStrings.EntityEqualityOnCompositeKeyEntitySubqueryNotSupported.

Solutions

  1. Rewrite as an explicit join or as separate Any-based predicates over each key property.
  2. Project to a scalar comparable value (a derived single-column key) before Contains.
  3. Materialize the candidate set client-side and do the equality check in memory if volumes are small.

Example fix

// before
var q = db.Items.Where(i => allowed.Contains(i));
// after
var q = db.Items.Where(i =>
    allowed.Any(a => a.TenantId == i.TenantId && a.LocalId == i.LocalId));
Defensive patterns

Strategy: validation

Validate before calling

// rewrite Contains on composite-key entities as Any over all key parts
static IQueryable<T> MatchAny<T>(IQueryable<T> q, IReadOnlyList<T> candidates,
    Func<T, Expression<Func<T, bool>>> predicate)
    where T : class
{
    Expression<Func<T, bool>> body = _ => false;
    foreach (var c in candidates) body = CombineOr(body, predicate(c));
    return q.Where(body);
}

Type guard

static bool HasCompositeKey<T>(DbContext ctx)
    => (ctx.Model.FindEntityType(typeof(T))?.FindPrimaryKey()?.Properties.Count ?? 0) > 1;

Prevention

When it happens

Trigger: Queryable.Contains(entity) where the entity has a composite primary key and the source is a subquery/constant list, so EF cannot translate to a single-column IN.

Common situations: Join-like filtering using Contains against entities whose key spans multiple properties (e.g. TenantId + LocalId), or migrating a single-key Contains query after adding a second key column.

Related errors


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

Appendix: 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 3a2006ef56)