{"record":{"id":"a26c1dd50aedfa71","repo":"dotnet/efcore","slug":"entitytype-is-mapped-to-the-table-table-wh","errorCode":null,"errorMessage":"'{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.","messagePattern":"'(.+?)' is mapped to the table '(.+?)' while '(.+?)' is mapped to the table '(.+?)'\\. 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\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":2330,"sourceCode":"                if (rootSproc != null)\n                {\n                    var sproc = StoredProcedure.FindDeclaredStoredProcedure(entityType, storeObjectType);\n                    if (sproc != null\n                        && sproc != rootSproc)\n                    {\n                        throw new InvalidOperationException(\n                            RelationalStrings.StoredProcedureTphDuplicate(\n                                entityType.DisplayName(), rootEntityType.DisplayName(), rootId?.DisplayName()));\n                    }\n                }\n\n                continue;\n            }\n\n            switch (storeObjectType)\n            {\n                case StoreObjectType.Table:\n                    throw new InvalidOperationException(\n                        RelationalStrings.TphTableMismatch(\n                            entityType.DisplayName(), entityId.Value.DisplayName(),\n                            rootEntityType.DisplayName(), rootId?.DisplayName()));\n                case StoreObjectType.View:\n                    throw new InvalidOperationException(\n                        RelationalStrings.TphViewMismatch(\n                            entityType.DisplayName(), entityId.Value.DisplayName(),\n                            rootEntityType.DisplayName(), rootId?.DisplayName()));\n                case StoreObjectType.Function:\n                    throw new InvalidOperationException(\n                        RelationalStrings.TphDbFunctionMismatch(\n                            entityType.DisplayName(), entityId.Value.DisplayName(),\n                            rootEntityType.DisplayName(), rootId?.DisplayName()));\n                case StoreObjectType.InsertStoredProcedure:\n                case StoreObjectType.DeleteStoredProcedure:\n                case StoreObjectType.UpdateStoredProcedure:\n                    throw new InvalidOperationException(\n                        RelationalStrings.TphStoredProcedureMismatch(","sourceCodeStart":2312,"sourceCodeEnd":2348,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L2312-L2348","documentation":"In ValidateTphMapping for StoreObjectType.Table: a TPH hierarchy (one that has a discriminator property) must keep every type in the same table, because the discriminator column only exists once and queries rely on a single physical table. When a derived type resolves to a different Table StoreObjectIdentifier than the root, EF throws.","triggerScenarios":"Root has a discriminator property (TPH) but a derived type was configured with .ToTable(\"OtherTable\"), so its StoreObjectIdentifier differs from the root's. Detected by comparing entityId != rootId inside ValidateTphMapping.","commonSituations":"Adding .ToTable to a single derived type in a TPH hierarchy; scaffolding then later setting a discriminator; copy-paste of TPT config into a TPH model.","solutions":["Remove the .ToTable call from the derived type so it inherits the root's table.","If you want per-type tables, remove the discriminator property to move to TPT, then give each type its own table.","Explicitly map the derived type to the SAME table name/schema as the root to satisfy the equality check."],"exampleFix":"// before\nmodelBuilder.Entity<Base>().HasDiscriminator(b => b.Kind);\nmodelBuilder.Entity<Derived>().ToTable(\"Derived\"); // TPH but split table\n\n// after\nmodelBuilder.Entity<Base>().HasDiscriminator(b => b.Kind);\n// derived inherits the root table, OR drop discriminator and use TPT","handlingStrategy":"validation","validationCode":"bool TphAllTypesSameTable(DbContext context)\n{\n    foreach (var root in context.Model.GetEntityTypes()\n        .Where(e => e.BaseType == null && e.FindDiscriminatorProperty() != null && e.GetDerivedTypes().Any()))\n    {\n        var rootTable = StoreObjectIdentifier.Create(root, StoreObjectType.Table);\n        if (rootTable is null) continue;\n        foreach (var d in root.GetDerivedTypes())\n            if (StoreObjectIdentifier.Create(d, StoreObjectType.Table) is { } dt && dt != rootTable) return false;\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try { _ = context.Model; }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"mapped to the table\", StringComparison.Ordinal) && ex.Message.Contains(\"same table\", StringComparison.Ordinal))\n{\n    throw new InvalidOperationException(\"A TPH hierarchy must keep all types on one table. Remove the derived .ToTable or drop the discriminator for TPT.\", ex);\n}","preventionTips":["In TPH, never call .ToTable on derived types with a different name.","If per-type tables are needed, remove the discriminator to switch to TPT.","Add a model validation test asserting one table per TPH root."],"tags":["ef-core","model-validation","tph","tables","inheritance","relational"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}