dotnet/efcore · error · InvalidOperationException

ExecuteDeleteOnTableSplitting

ExecuteDeleteOnTableSplitting

Error message

The operation 'ExecuteDelete' is being applied on the table '{tableName}' which contains data for multiple entity types. Applying this delete operation will also delete data for other entity type(s), hence it is not supported.

What it means

Thrown by TranslateExecuteDelete when the target table is shared by multiple entity types (table splitting / shared tables, e.g. an owned entity sharing its principal's table, or two entities mapped to the same table) and deleting would affect rows belonging to other entity types. EF refuses to issue a delete that could corrupt data for the co-located types.

Source

Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs:100

                tableExpression = tableExpression is JoinExpressionBase join
                    ? join.Update(unwrappedTableExpression)
                    : unwrappedTableExpression;
                var newTables = selectExpression.Tables.ToList();
                newTables[tableIndex] = tableExpression;

                // Note that we need to keep the select mutable, because if IsValidSelectExpressionForExecuteDelete below returns false,
                // we need to compose on top of it.
                selectExpression.SetTables(newTables);
            }

            // Finally, check if the provider has a native translation for the delete represented by the select expression.
            // The default relational implementation handles simple, universally-supported cases (i.e. no operators except for predicate).
            // Providers may override IsValidSelectExpressionForExecuteDelete to add support for more cases via provider-specific DELETE syntax.
            if (IsValidSelectExpressionForExecuteDelete(selectExpression))
            {
                if (AreOtherNonOwnedEntityTypesInTheTable(entityType.GetRootType(), targetTable))
                {
                    throw new InvalidOperationException(
                        RelationalStrings.ExecuteDeleteOnTableSplitting(unwrappedTableExpression.Table.SchemaQualifiedName));
                }

                selectExpression.ReplaceProjection([]);
                selectExpression.ApplyProjection();

                return new DeleteExpression(unwrappedTableExpression, selectExpression);
            }
        }

        // We can't translate to a simple delete (e.g. the provider doesn't support one of the clauses).
        // As a fallback, we place the original query in a Contains subquery, which will get translated via the regular entity equality/
        // containment mechanism (InExpression for non-composite keys, Any for composite keys)
        var pk = entityType.FindPrimaryKey();
        if (pk == null)
        {
            throw new InvalidOperationException(
                RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator(

View on GitHub (pinned to dbf9771522)

Solutions

  1. Map only one entity type per table, or ensure shared tables only contain owned dependents (owned types are excluded from this check).
  2. Load entities and delete via SaveChanges, which orders operations to respect shared-table invariants.
  3. Use raw SQL (Database.ExecuteSqlRaw) only if you can guarantee no cross-type row corruption.
  4. Restructure so the deletable entity has its own dedicated table.

Example fix

// before - Customer and CustomerAudit share the Customers table
modelBuilder.Entity<Customer>().ToTable("Customers");
modelBuilder.Entity<CustomerAudit>().ToTable("Customers");
await db.Customers.Where(c => c.Id == id).ExecuteDeleteAsync();
// after - give each entity its own table
modelBuilder.Entity<CustomerAudit>().ToTable("CustomerAudits");
await db.Customers.Where(c => c.Id == id).ExecuteDeleteAsync();
Defensive patterns

Strategy: validation

Validate before calling

static bool TableHasOtherNonOwnedRoots(IEntityType et)
{
    foreach (var tm in et.GetTableMappings())
        foreach (var m in tm.Table.EntityTypeMappings)
            if (m.TypeBase is IEntityType other && other.GetRootType() != et.GetRootType() && !other.IsOwned())
                return true;
    return false;
}

if (TableHasOtherNonOwnedRoots(entityType))
    throw new InvalidOperationException("ExecuteDelete would affect other entity types sharing the table.");

Try / catch

try { await db.Customers.Where(c => c.Id == id).ExecuteDeleteAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("multiple entity types"))
{
    var c = await db.Customers.FindAsync(id);
    db.Customers.Remove(c);
    await db.SaveChangesAsync();
}

Prevention

When it happens

Trigger: Calling ExecuteDelete on an entity whose table is also mapped (non-owned, different root) to another entity type; or table sharing where the principal and a non-owned dependent share the same table.

Common situations: Table splitting scenarios (multiple entities mapped to one physical table); joining two entity types to the same table for read convenience; owned entities whose owner shares the table with another principal.

Related errors


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