{"record":{"id":"800ef22109d3ecf6","repo":"dotnet/efcore","slug":"both-entitytype-and-otherentitytype-are-ma-800ef2","errorCode":null,"errorMessage":"Both '{entityType}' and '{otherEntityType}' are mapped to the stored procedure '{sproc}'. All the entity types in a non-TPH hierarchy (one that doesn't have a discriminator) must be mapped to different stored procedures. See https://go.microsoft.com/fwlink/?linkid=2130430 for more information.","messagePattern":"Both '(.+?)' and '(.+?)' are mapped to the stored procedure '(.+?)'\\. All the entity types in a non-TPH hierarchy \\(one that doesn't have a discriminator\\) must be mapped to different stored procedures\\. 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":2251,"sourceCode":"                continue;\n            }\n\n            if (derivedTypes.TryGetValue(storeObject.Value, out var otherType))\n            {\n                switch (storeObjectType)\n                {\n                    case StoreObjectType.Table:\n                        throw new InvalidOperationException(\n                            RelationalStrings.NonTphTableClash(\n                                entityType.DisplayName(), otherType.DisplayName(), storeObject.Value.DisplayName()));\n                    case StoreObjectType.View:\n                        throw new InvalidOperationException(\n                            RelationalStrings.NonTphViewClash(\n                                entityType.DisplayName(), otherType.DisplayName(), storeObject.Value.DisplayName()));\n                    case StoreObjectType.InsertStoredProcedure:\n                    case StoreObjectType.DeleteStoredProcedure:\n                    case StoreObjectType.UpdateStoredProcedure:\n                        throw new InvalidOperationException(\n                            RelationalStrings.NonTphStoredProcedureClash(\n                                entityType.DisplayName(), otherType.DisplayName(), storeObject.Value.DisplayName()));\n                }\n            }\n\n            if (isTpc)\n            {\n                var rowInternalFk = entityType.FindDeclaredReferencingRowInternalForeignKeys(storeObject.Value)\n                    .FirstOrDefault();\n                if (rowInternalFk != null\n                    && entityType.GetDirectlyDerivedTypes().Any())\n                {\n                    throw new InvalidOperationException(\n                        RelationalStrings.TpcTableSharing(\n                            rowInternalFk.DeclaringEntityType.DisplayName(),\n                            storeObject.Value.DisplayName(),\n                            rowInternalFk.PrincipalEntityType.DisplayName()));\n                }","sourceCodeStart":2233,"sourceCodeEnd":2269,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L2233-L2269","documentation":"Thrown inside ValidateNonTphMapping when two entity types in a non-TPH hierarchy resolve to the same Insert/Delete/Update stored procedure StoreObjectIdentifier. In TPT/TPC each type must have its own distinct stored procedure for each operation; sharing is rejected.","triggerScenarios":"Two types in a TPT/TPC hierarchy both call InsertUsingStoredProcedure(\"sproc\")/DeleteUsingStoredProcedure/UpdateUsingStoredProcedure with the same name; sibling types resolving to the same sproc.","commonSituations":"Centralizing CRUD into shared stored procedures while using TPT/TPC; copy-pasting stored procedure mappings; assuming one sproc can serve the whole non-TPH hierarchy.","solutions":["Map each type to its own stored procedure name in a non-TPH hierarchy.","Switch to TPH (with the sproc defined on the root) if you want a single shared stored procedure.","Remove duplicate *UsingStoredProcedure configuration and let each type define its own."],"exampleFix":"// before (TPT, shared sproc)\nmodelBuilder.UseTptMappingStrategy();\nmodelBuilder.Entity<Customer>().InsertUsingStoredProcedure(\"sp_Upsert\");\nmodelBuilder.Entity<Vendor>().InsertUsingStoredProcedure(\"sp_Upsert\"); // throws 428\n\n// after\nmodelBuilder.Entity<Customer>().InsertUsingStoredProcedure(\"sp_InsertCustomer\");\nmodelBuilder.Entity<Vendor>().InsertUsingStoredProcedure(\"sp_InsertVendor\");","handlingStrategy":"validation","validationCode":"foreach (var root in modelBuilder.Model.GetEntityTypes().Where(t => t.BaseType == null))\n{\n    var strategy = root.GetMappingStrategy();\n    if (strategy != RelationalAnnotationNames.TptMappingStrategy\n        && strategy != RelationalAnnotationNames.TpcMappingStrategy) continue;\n    foreach (var sot in new[] { StoreObjectType.InsertStoredProcedure, StoreObjectType.DeleteStoredProcedure, StoreObjectType.UpdateStoredProcedure })\n    {\n        var sprocs = new HashSet<string>();\n        foreach (var et in root.GetDerivedTypesInclusive())\n        {\n            var so = StoreObjectIdentifier.Create(et, sot);\n            if (so != null && !sprocs.Add(so.Value.Name))\n                throw new InvalidOperationException($\"Sproc '{so.Value.Name}' ({sot}) used by multiple types in {root.DisplayName()}.\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["In TPT/TPC, give each type its own stored procedure per operation.","Use TPH (sproc on the root) if one shared stored procedure is intended.","Validate sproc uniqueness per hierarchy in a model test."],"tags":["ef-core","model-validation","mapping","inheritance","tpt","tpc","stored-procedures"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}