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

  1. Move the table sharing onto a leaf type (a type with no derived types).
  2. Remove the inheritance from the type that shares a table so it becomes a leaf.
  3. 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

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


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

View on GitHub (pinned to 3a2006ef56)