dotnet/efcore · error · InvalidOperationException

Both ' ' and ' ' are mapped to the table ' '. All the…

Error message

Both '{entityType}' and '{otherEntityType}' are mapped to the table '{table}'. All the entity types in a non-TPH hierarchy (one that doesn't have a discriminator) must be mapped to different tables. See https://go.microsoft.com/fwlink/?linkid=2130430 for more information.

What it means

Thrown inside ValidateNonTphMapping when two different entity types within the same non-TPH hierarchy resolve to the same Table StoreObjectIdentifier (collision detected via derivedTypes dictionary). In TPT/TPC every type must map to its own distinct table, so a clash is rejected.

Solutions

  1. Give each entity type a unique table name in the non-TPH hierarchy.
  2. Switch to TPH (UseTphMappingStrategy) if you genuinely want them all in one table.
  3. Remove explicit ToTable overrides that force the collision and let conventions assign distinct names.

Example fix

// before (TPT, clash)
modelBuilder.UseTptMappingStrategy();
modelBuilder.Entity<Customer>().ToTable("People");
modelBuilder.Entity<Vendor>().ToTable("People"); // throws 426

// after
modelBuilder.Entity<Customer>().ToTable("Customers");
modelBuilder.Entity<Vendor>().ToTable("Vendors");
Defensive patterns

Strategy: validation

Validate before calling

foreach (var root in modelBuilder.Model.GetEntityTypes().Where(t => t.BaseType == null))
{
    var strategy = root.GetMappingStrategy();
    if (strategy != RelationalAnnotationNames.TptMappingStrategy
        && strategy != RelationalAnnotationNames.TpcMappingStrategy) continue;
    var tables = new HashSet<string>();
    foreach (var et in root.GetDerivedTypesInclusive())
    {
        var so = StoreObjectIdentifier.Create(et, StoreObjectType.Table);
        if (so != null && !tables.Add(so.Value.Name))
            throw new InvalidOperationException($"Table '{so.Value.Name}' used by multiple types in {root.DisplayName()} hierarchy.");
    }
}

Prevention

When it happens

Trigger: Two types in a TPT/TPC hierarchy both call ToTable("SameName"), or default table-name conventions produce the same name for sibling types; sharing a table via explicit ToTable overrides in a non-TPH hierarchy.

Common situations: Manually mapping derived types to the same table while using TPT/TPC; rename/refactor that made two types collide on pluralization; trying to simulate TPH by forcing the same table under TPT.

Related errors


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

Appendix: source

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

                    && entityType.GetDerivedTypes().Any(derived => StoreObjectIdentifier.Create(derived, storeObjectType) != null))
                {
                    throw new InvalidOperationException(
                        RelationalStrings.UnmappedNonTPHOwner(
                            entityType.DisplayName(),
                            unmappedOwnedType.FindOwnership()!.PrincipalToDependent?.Name,
                            unmappedOwnedType.DisplayName(),
                            storeObjectType));
                }

                continue;
            }

            if (derivedTypes.TryGetValue(storeObject.Value, out var otherType))
            {
                switch (storeObjectType)
                {
                    case StoreObjectType.Table:
                        throw new InvalidOperationException(
                            RelationalStrings.NonTphTableClash(
                                entityType.DisplayName(), otherType.DisplayName(), storeObject.Value.DisplayName()));
                    case StoreObjectType.View:
                        throw new InvalidOperationException(
                            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)

View on GitHub (pinned to 3a2006ef56)