dotnet/efcore · error · InvalidOperationException

' ' is mapped to the table ' ' while ' ' is mapped to the…

Error message

'{entityType}' is mapped to the table '{table}' while '{otherEntityType}' is mapped to the table '{otherTable}'. Map all the entity types in the hierarchy to the same table, or remove the discriminator and map them all to different tables. See https://go.microsoft.com/fwlink/?linkid=2130430 for more information.

What it means

Thrown inside ValidateTphMapping when a derived entity type resolves to a different Table StoreObjectIdentifier than the root (entityId != rootId). TPH requires the entire hierarchy to share a single table, so any derived type mapped to another table breaks the invariant.

Solutions

  1. Remove the ToTable override on the derived type so it inherits the root's table.
  2. Switch the whole hierarchy to TPT (UseTptMappingStrategy) if different tables are intended.
  3. Map the root to the same table as the derived types so they all match.

Example fix

// before (TPH but derived type on different table)
modelBuilder.Entity<Person>().ToTable("People");
modelBuilder.Entity<Customer>().ToTable("Customers"); // throws 432

// after (intended different tables -> TPT)
modelBuilder.UseTptMappingStrategy();
modelBuilder.Entity<Person>().ToTable("People");
modelBuilder.Entity<Customer>().ToTable("Customers");
Defensive patterns

Strategy: validation

Validate before calling

foreach (var root in modelBuilder.Model.GetEntityTypes().Where(t => t.BaseType == null))
{
    if (root.FindDiscriminatorProperty() == null) continue; // not TPH
    var rootSo = StoreObjectIdentifier.Create(root, StoreObjectType.Table);
    if (rootSo == null) continue;
    foreach (var et in root.GetDerivedTypes())
    {
        var so = StoreObjectIdentifier.Create(et, StoreObjectType.Table);
        if (so != null && so.Value != rootSo.Value)
            throw new InvalidOperationException($"{et.DisplayName()} maps to {so.Value.DisplayName()} but root maps to {rootSo.Value.DisplayName()} (TPH).");
    }
}

Prevention

When it happens

Trigger: A TPH hierarchy where a derived type calls ToTable("OtherTable") or is otherwise mapped to a different table than the root; default convention overridden per type.

Common situations: Adding ToTable on a derived type while staying on TPH; partial migration to TPT that forgot UseTptMappingStrategy; view-backed read models pulled into a TPH hierarchy.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/a26c1dd50aedfa71. Report an issue: GitHub.

Appendix: source

Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:2330

                if (rootSproc != null)
                {
                    var sproc = StoredProcedure.FindDeclaredStoredProcedure(entityType, storeObjectType);
                    if (sproc != null
                        && sproc != rootSproc)
                    {
                        throw new InvalidOperationException(
                            RelationalStrings.StoredProcedureTphDuplicate(
                                entityType.DisplayName(), rootEntityType.DisplayName(), rootId?.DisplayName()));
                    }
                }

                continue;
            }

            switch (storeObjectType)
            {
                case StoreObjectType.Table:
                    throw new InvalidOperationException(
                        RelationalStrings.TphTableMismatch(
                            entityType.DisplayName(), entityId.Value.DisplayName(),
                            rootEntityType.DisplayName(), rootId?.DisplayName()));
                case StoreObjectType.View:
                    throw new InvalidOperationException(
                        RelationalStrings.TphViewMismatch(
                            entityType.DisplayName(), entityId.Value.DisplayName(),
                            rootEntityType.DisplayName(), rootId?.DisplayName()));
                case StoreObjectType.Function:
                    throw new InvalidOperationException(
                        RelationalStrings.TphDbFunctionMismatch(
                            entityType.DisplayName(), entityId.Value.DisplayName(),
                            rootEntityType.DisplayName(), rootId?.DisplayName()));
                case StoreObjectType.InsertStoredProcedure:
                case StoreObjectType.DeleteStoredProcedure:
                case StoreObjectType.UpdateStoredProcedure:
                    throw new InvalidOperationException(
                        RelationalStrings.TphStoredProcedureMismatch(

View on GitHub (pinned to 3a2006ef56)