dotnet/efcore · error · InvalidOperationException
The entity type ' ' is mapped to ' '. However one of its…
Error message
The entity type '{dependentType}' is mapped to '{storeObject}'. However one of its derived types '{derivedType}' is mapped to '{otherStoreObject}'. Hierarchies using table-sharing cannot be mapped using the TPC mapping strategy. What it means
Thrown at the end of ValidateNonTphMapping when, in a TPC hierarchy, the root entity has row internal foreign keys (shares its table with a principal), and more than one derived type exists (derivedTypes.Count > 1). TPC requires each concrete type to be fully self-contained, so a root that shares a table while being inherited by multiple types is unsupported.
Solutions
- Stop table sharing at the TPC root (remove the ownership/splitting or map owned data separately).
- Reduce to a single derived type (not usually intended — prefer the first option).
- Switch the hierarchy from TPC to TPT/TPH if table sharing at the root is required.
Example fix
// before (TPC, root shares table, multiple derived)
modelBuilder.Entity<Person>().ToTable("People").OwnsOne(p => p.Audit, a => a.ToTable("People"));
modelBuilder.UseTpcMappingStrategy();
// Customer and Vendor derive from Person -> throws 430
// after - no table sharing at root under TPC
modelBuilder.Entity<Person>().OwnsOne(p => p.Audit, a => a.ToTable("People_Audit")); 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 (StoreObjectType sot in new[] { StoreObjectType.Table, StoreObjectType.View })
{
var rootSo = StoreObjectIdentifier.Create(root, sot);
if (rootSo == null) continue;
if (!root.FindRowInternalForeignKeys(rootSo.Value).Any()) continue;
var derivedCount = root.GetDerivedTypesInclusive().Count();
if (derivedCount > 1)
throw new InvalidOperationException($"Root {root.DisplayName()} shares {sot} under TPC but has {derivedCount} types in hierarchy.");
}
} Prevention
- Avoid table sharing at the TPC root when there are multiple derived types.
- Refactor owned/split data off the root, or switch strategy.
- Add a model test covering TPC + sharing scenarios.
When it happens
Trigger: The root of a TPC hierarchy participates in table sharing (FindRowInternalForeignKeys on the root store object returns any) and has multiple derived concrete types mapping into the hierarchy.
Common situations: An abstract/concrete root that owns or shares a table with another entity, combined with multiple derived types under UseTpcMappingStrategy(); refactor that added a derived type to an already-splitting TPC root.
Related errors
- The entity type ' ' is mapped to ' '. However the principal…
- 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…
- Entity type ' ' has a split mapping for ' ', but it also…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/8e81d5fafd7cb37e.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:2286
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 > 1
&& rootEntityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy)
{
var derivedTypePair = derivedTypes.First(kv => kv.Value != rootEntityType);
throw new InvalidOperationException(
RelationalStrings.TpcTableSharingDependent(
rootEntityType.DisplayName(),
rootStoreObject.Value.DisplayName(),
derivedTypePair.Value.DisplayName(),
derivedTypePair.Key.DisplayName()));
}
}
private static void ValidateTphMapping(IEntityType rootEntityType, StoreObjectType storeObjectType)
{
var isSproc = storeObjectType is StoreObjectType.DeleteStoredProcedure
or StoreObjectType.InsertStoredProcedure
or StoreObjectType.UpdateStoredProcedure;
var rootSproc = isSproc ? StoredProcedure.FindDeclaredStoredProcedure(rootEntityType, storeObjectType) : null;
var rootId = StoreObjectIdentifier.Create(rootEntityType, storeObjectType);
foreach (var entityType in rootEntityType.GetDerivedTypes())
{
var entityId = StoreObjectIdentifier.Create(entityType, storeObjectType);View on GitHub (pinned to 3a2006ef56)