dotnet/efcore · error · InvalidOperationException
The entity type ' ' is mapped to ' '. However the principal…
Error message
The entity type '{dependentType}' is mapped to '{storeObject}'. However the principal entity type '{principalEntityType}' is also mapped to '{storeObject}' and it's using the TPC mapping strategy. Entity types in a TPC hierarchy can use table-sharing only if they have no derived types. What it means
Thrown inside ValidateNonTphMapping when, under the TPC strategy, an entity type has a row internal foreign key (meaning it shares a table with its principal via table splitting) AND that same entity type has directly derived types. TPC permits table sharing only for leaf types that have no derived types, because a shared table cannot be replicated per concrete type.
Solutions
- Move the table sharing onto a leaf type (a type with no derived types).
- Remove the inheritance from the type that shares a table so it becomes a leaf.
- Switch the hierarchy away from TPC, or stop table-sharing on the non-leaf type.
Example fix
// before (TPC, non-leaf shares table)
modelBuilder.UseTpcMappingStrategy();
modelBuilder.Entity<Person>().OwnsOne(p => p.Contact, c => c.ToTable("People"));
// Person has derived types -> throws 429
// after - share on a leaf only, or stop splitting the non-leaf
modelBuilder.Entity<Customer>().OwnsOne(p => p.Contact, c => c.ToTable("Customers")); Defensive patterns
Strategy: validation
Validate before calling
foreach (var root in modelBuilder.Model.GetEntityTypes().Where(t => t.BaseType == null))
{
if (root.GetMappingStrategy() != RelationalAnnotationNames.TpcMappingStrategy) continue;
foreach (var et in root.GetDerivedTypesInclusive())
{
if (!et.GetDirectlyDerivedTypes().Any()) continue;
foreach (StoreObjectType sot in new[] { StoreObjectType.Table, StoreObjectType.View })
{
var so = StoreObjectIdentifier.Create(et, sot);
if (so == null) continue;
if (et.FindDeclaredReferencingRowInternalForeignKeys(so.Value).Any())
throw new InvalidOperationException($"{et.DisplayName()} shares a {sot} but has derived types under TPC.");
}
}
} Prevention
- Do not place table sharing on non-leaf types in a TPC hierarchy.
- Move ownership/splitting to leaf types, or stop sharing the table.
- Test the model build after combining TPC with owned/split types.
When it happens
Trigger: A TPC hierarchy where an intermediate (non-leaf) type participates in table sharing (FindDeclaredReferencingRowInternalForeignKeys returns a key) and also has GetDirectlyDerivedTypes().Any() == true.
Common situations: Combining owned/table-splitting with a TPC hierarchy on a base type that is itself inherited from; converting a splitting scenario into TPC; an intermediate entity that owns another entity and is derived from.
Related errors
- The entity type ' ' is mapped to ' '. However one of its…
- The entity type ' ' is not mapped, so by default the owned…
- Both ' ' and ' ' are mapped to the table ' '. All the…
- Both ' ' and ' ' are mapped to the stored procedure ' '…
- Both ' ' and ' ' are mapped to the view ' '. All the entity…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/9dfaeea02cf7421c.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:2264
RelationalStrings.NonTphViewClash(
entityType.DisplayName(), otherType.DisplayName(), storeObject.Value.DisplayName()));
case StoreObjectType.InsertStoredProcedure:
case StoreObjectType.DeleteStoredProcedure:
case StoreObjectType.UpdateStoredProcedure:
throw new InvalidOperationException(
RelationalStrings.NonTphStoredProcedureClash(
entityType.DisplayName(), otherType.DisplayName(), storeObject.Value.DisplayName()));
}
}
if (isTpc)
{
var rowInternalFk = entityType.FindDeclaredReferencingRowInternalForeignKeys(storeObject.Value)
.FirstOrDefault();
if (rowInternalFk != null
&& entityType.GetDirectlyDerivedTypes().Any())
{
throw new InvalidOperationException(
RelationalStrings.TpcTableSharing(
rowInternalFk.DeclaringEntityType.DisplayName(),
storeObject.Value.DisplayName(),
rowInternalFk.PrincipalEntityType.DisplayName()));
}
}
derivedTypes[storeObject.Value] = entityType;
}
var rootStoreObject = StoreObjectIdentifier.Create(rootEntityType, storeObjectType);
if (rootStoreObject == null)
{
return;
}
if (rootEntityType.FindRowInternalForeignKeys(rootStoreObject.Value).Any()
&& derivedTypes.Count > 1View on GitHub (pinned to 3a2006ef56)