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

  1. Stop table sharing at the TPC root (remove the ownership/splitting or map owned data separately).
  2. Reduce to a single derived type (not usually intended — prefer the first option).
  3. 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

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


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)