dotnet/efcore · error · InvalidOperationException
ExecuteOperationOnTPC
ExecuteOperationOnTPC
Error message
The operation '{operation}' is being applied on entity type '{entityType}', which is using the TPC mapping strategy and is not a leaf type. 'ExecuteDelete'/'ExecuteUpdate' operations on entity types participating in TPC hierarchies is only supported for leaf types. What it means
Thrown by TranslateExecuteDelete when the target entity type uses the TPC (Table-Per-Concrete-Type) strategy and is NOT a leaf - i.e. it has derived types. In TPC, ExecuteDelete/ExecuteUpdate can only safely target a leaf type (one concrete table); a non-leaf has no single concrete table to modify, so derived rows elsewhere would be missed.
Source
Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs:36
}
if (entityType.IsMappedToJson())
{
throw new InvalidOperationException(
RelationalStrings.ExecuteOperationOnOwnedJsonIsNotSupported("ExecuteDelete", entityType.DisplayName()));
}
switch (entityType.GetMappingStrategy())
{
case RelationalAnnotationNames.TptMappingStrategy:
throw new InvalidOperationException(
RelationalStrings.ExecuteOperationOnTPT(
nameof(EntityFrameworkQueryableExtensions.ExecuteDelete),
entityType.DisplayName()));
// Note that we do allow TPC if the target is a leaf type
case RelationalAnnotationNames.TpcMappingStrategy when entityType.GetDirectlyDerivedTypes().Any():
throw new InvalidOperationException(
RelationalStrings.ExecuteOperationOnTPC(
nameof(EntityFrameworkQueryableExtensions.ExecuteDelete),
entityType.DisplayName()));
}
// Find the table model that maps to the entity type; there must be exactly one (e.g. no entity splitting).
var targetTable = entityType.GetTableMappings().ToList() switch
{
[] => throw new InvalidOperationException(
RelationalStrings.ExecuteUpdateDeleteOnEntityNotMappedToTable(entityType.DisplayName())),
[var singleTableMapping] => singleTableMapping.Table,
_ => throw new InvalidOperationException(
RelationalStrings.ExecuteOperationOnEntitySplitting(
nameof(EntityFrameworkQueryableExtensions.ExecuteDelete), entityType.DisplayName())),
};
var selectExpression = (SelectExpression)source.QueryExpression;
// Find the table expression in the SelectExpression that corresponds to the projected entity type.View on GitHub (pinned to dbf9771522)
Solutions
- Target a leaf type in the TPC hierarchy (a type with no derived types).
- Issue separate ExecuteDelete calls per concrete leaf type, or union them explicitly.
- Switch the hierarchy to TPH for full base-type bulk delete support.
- Load and delete via SaveChanges if you must delete across the whole TPC hierarchy.
Example fix
// before - Animal is the TPC root, has derived Dog/Cat await db.Animals.Where(a => a.Id == id).ExecuteDeleteAsync(); // after - delete each leaf explicitly, or query the right leaf await db.Set<Dog>().Where(d => d.Id == id).ExecuteDeleteAsync(); await db.Set<Cat>().Where(c => c.Id == id).ExecuteDeleteAsync();
Defensive patterns
Strategy: validation
Validate before calling
static bool IsTpcLeaf(IEntityType et)
=> et.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy
&& !et.GetDirectlyDerivedTypes().Any();
if (!IsTpcLeaf(entityType))
throw new InvalidOperationException("ExecuteDelete on TPC requires a leaf type."); Try / catch
try { await db.Animals.Where(predicate).ExecuteDeleteAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("TPC mapping strategy"))
{
// fan out to leaf types explicitly
await db.Set<Dog>().Where(predicate).ExecuteDeleteAsync();
await db.Set<Cat>().Where(predicate).ExecuteDeleteAsync();
} Prevention
- Target leaf types when bulk-deleting in TPC hierarchies.
- Add a helper that enumerates concrete leaves and deletes each.
- Use TPH when you need to delete from the hierarchy root.
- Write tests covering base-vs-leaf delete in TPC.
When it happens
Trigger: Configuring a hierarchy with .UseTpcMappingStrategy() and calling ExecuteDelete/ExecuteUpdate on a base or intermediate type that has directly derived types.
Common situations: Choosing TPC and then trying to bulk-delete from the root entity; targeting an abstract base or a non-leaf node.
Related errors
- ExecuteOperationOnTPT
- ExecuteOperationOnTPC
- ExecuteOperationOnOwnedJsonIsNotSupported
- ExecuteUpdateDeleteOnEntityNotMappedToTable
- Using '{methodName}' on DbSet of '{entityType}' is not suppo
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/79714ac0cbde0173.
Report an issue: GitHub.