dotnet/efcore · error · InvalidOperationException

The entity type '{dependentType}' is mapped to '{storeObject

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

Also in ValidateNonTphMapping for TPC: if the root entity has row-internal foreign keys (i.e. table-sharing) and more than one entity type in the hierarchy is mapped, the model is rejected because TPC requires each concrete type in its own table and cannot represent a shared root table across a hierarchy. The first non-root derived mapping is reported.

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 dbf9771522)

Solutions

  1. Break the table sharing on the root so FindRowInternalForeignKeys is empty: give the root (and any shared principal) distinct tables.
  2. Switch the hierarchy to TPT or TPH which can coexist with shared root tables.
  3. Collapse the hierarchy so there is only one mapped type (no derived mappings).

Example fix

// before
modelBuilder.Entity<Customer>().UseTpcMappingStrategy().ToTable("Contacts"); // Contacts shared

// after
modelBuilder.Entity<Customer>().UseTpcMappingStrategy().ToTable("Customers"); // exclusive table
Defensive patterns

Strategy: validation

Validate before calling

bool TpcRootNotTableSharedWithHierarchy(DbContext context)
{
    foreach (var root in context.Model.GetEntityTypes()
        .Where(e => e.BaseType == null && e.GetMappingStrategy() == "TPC"))
    {
        var so = StoreObjectIdentifier.Create(root, StoreObjectType.Table);
        if (so is null) continue;
        bool shares = root.FindRowInternalForeignKeys(so.Value).Any();
        int mappedCount = root.GetDerivedTypesInclusive().Count(d => StoreObjectIdentifier.Create(d, StoreObjectType.Table) != null);
        if (shares && mappedCount > 1) return false;
    }
    return true;
}

Try / catch

try { _ = context.Model; }
catch (InvalidOperationException ex) when (ex.Message.Contains("TPC", StringComparison.Ordinal) && ex.Message.Contains("table-sharing", StringComparison.Ordinal) && ex.Message.Contains("derived", StringComparison.Ordinal))
{
    throw new InvalidOperationException("A TPC root shares its table while multiple derived types are mapped. Break the sharing or switch to TPT.", ex);
}

Prevention

When it happens

Trigger: Root entity has TPC strategy, FindRowInternalForeignKeys returns at least one row-internal FK on the root store object, and derivedTypes.Count > 1 (multiple hierarchy members mapped). Typically appears when the root table is shared with another entity but you still want TPC over its derived types.

Common situations: Adding .UseTpcMappingStrategy() to an entity that also participates in table sharing (e.g. owned/inline or joint table) with derived types; migrating a shared-table design to TPC.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/8e81d5fafd7cb37e. Report an issue: GitHub.