dotnet/efcore · error · InvalidOperationException
ExecuteOperationOnTPT
ExecuteOperationOnTPT
Error message
The operation '{operation}' is being applied on entity type '{entityType}', which is using the TPT mapping strategy. 'ExecuteDelete'/'ExecuteUpdate' operations on hierarchies mapped as TPT are not supported. What it means
Thrown by TranslateExecuteDelete when the target entity type uses the TPT (Table-Per-Type) mapping strategy. EF cannot translate ExecuteDelete/ExecuteUpdate for TPT because rows for a derived type are spread across multiple tables and a single bulk DML statement cannot correctly cascade across them.
Source
Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs:29
protected override DeleteExpression TranslateExecuteDelete(ShapedQueryExpression source)
{
source = source.UpdateShaperExpression(new IncludePruner().Visit(source.ShaperExpression));
if (source.ShaperExpression is not StructuralTypeShaperExpression { StructuralType: IEntityType entityType } shaper)
{
throw new InvalidOperationException(RelationalStrings.ExecuteDeleteOnNonEntityType);
}
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,View on GitHub (pinned to dbf9771522)
Solutions
- Switch the hierarchy to TPH (single table) which fully supports ExecuteDelete/ExecuteUpdate.
- Use TPC and target only leaf types (ExecuteDelete supports TPC leaves).
- Load entities and delete/update via the change tracker (SaveChanges) which handles multi-table TPT correctly.
- Restructure so the deletable type is not part of a TPT hierarchy.
Example fix
// before modelBuilder.Entity<Animal>().UseTptMappingStrategy(); await db.Animals.Where(a => a.Id == id).ExecuteDeleteAsync(); // after - TPH supports bulk delete modelBuilder.Entity<Animal>().UseTphMappingStrategy(); await db.Animals.Where(a => a.Id == id).ExecuteDeleteAsync(); // or fall back to tracked delete var a = await db.Animals.FindAsync(id); db.Animals.Remove(a); await db.SaveChangesAsync();
Defensive patterns
Strategy: validation
Validate before calling
var strategy = entityType.GetMappingStrategy();
if (strategy == RelationalAnnotationNames.TptMappingStrategy)
throw new InvalidOperationException("ExecuteDelete/ExecuteUpdate not supported on TPT hierarchies; use TPH or SaveChanges."); Try / catch
try { await db.Animals.Where(a => a.Id == id).ExecuteDeleteAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("TPT mapping strategy"))
{
var a = await db.Animals.FindAsync(id);
db.Animals.Remove(a);
await db.SaveChangesAsync();
} Prevention
- Use TPH if you need bulk delete/update across a hierarchy.
- Reserve TPT for hierarchies updated via SaveChanges only.
- Add model-validation rules that flag ExecuteDelete usage on TPT types.
- Document mapping-strategy trade-offs for bulk DML in your team.
When it happens
Trigger: Configuring an inheritance hierarchy with .UseTptMappingStrategy() (or per-type tables) and then calling ExecuteDelete/ExecuteUpdate on any type in that hierarchy.
Common situations: Migrating from TPH to TPT for storage reasons and forgetting that bulk DML is unsupported there; applying ExecuteDelete to a base entity in a TPT hierarchy.
Related errors
- ExecuteOperationOnTPC
- ExecuteOperationOnOwnedJsonIsNotSupported
- ExecuteUpdateDeleteOnEntityNotMappedToTable
- ExecuteOperationOnTPC
- Using '{methodName}' on DbSet of '{entityType}' is not suppo
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/7f7cc845edd3353b.
Report an issue: GitHub.