dotnet/efcore · error · InvalidOperationException

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

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

In ValidateNonTphMapping, when the hierarchy is TPC, EF allows a dependent entity to share a table with its principal via a row-internal foreign key ONLY if the dependent has no derived types of its own. If a dependent that table-shares also has directly derived types, EF cannot keep the TPC 'one table per concrete type' guarantee and throws.

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 > 1

View on GitHub (pinned to dbf9771522)

Solutions

  1. Move the dependent to its own table so it no longer shares with the principal: modelBuilder.Entity<Dependent>().ToTable("Dependents").
  2. Remove inheritance from the dependent (collapse the derived types) so it remains a leaf shared type.
  3. Switch the hierarchy away from TPC (e.g. to TPT) if the shared-table-with-inheritance pattern is required.

Example fix

// before
modelBuilder.Entity<Order>().UseTpcMappingStrategy();
modelBuilder.Entity<OrderDetail>().HasBaseType<OrderDetailBase>().ToTable("Orders"); // shares + derives

// after
modelBuilder.Entity<OrderDetail>().HasBaseType<OrderDetailBase>().ToTable("OrderDetails");
Defensive patterns

Strategy: validation

Validate before calling

bool TpcSharedDependentsAreLeaves(DbContext context)
{
    foreach (var root in context.Model.GetEntityTypes()
        .Where(e => e.BaseType == null && e.GetMappingStrategy() == "TPC"))
    {
        foreach (var et in root.GetDerivedTypesInclusive())
        {
            var so = StoreObjectIdentifier.Create(et, StoreObjectType.Table);
            if (so is null) continue;
            bool sharesWithPrincipal = et.FindDeclaredReferencingRowInternalForeignKeys(so.Value).Any();
            if (sharesWithPrincipal && et.GetDirectlyDerivedTypes().Any()) 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))
{
    throw new InvalidOperationException("A table-shared dependent in a TPC hierarchy has derived types. Move it to its own table or remove inheritance from it.", ex);
}

Prevention

When it happens

Trigger: A dependent entity (typically an owned/inline type via row-internal FK) shares a table with its principal AND GetDirectlyDerivedTypes().Any() is true on it, while the root has TPC mapping strategy. Triggered when you mix table/owner sharing with TPC and then add an inheritance chain on the shared dependent.

Common situations: Adding a base+derived relationship to an owned type that was previously table-shared in a TPC hierarchy; converting an owned type to a hierarchy while keeping its ToTable equal to the principal's.

Related errors


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