{"record":{"id":"afb5991ff96a4b80","repo":"dotnet/efcore","slug":"both-entity-type-entitytype1-and-entitytype2","errorCode":null,"errorMessage":"Both entity type '{entityType1}' and '{entityType2}' were configured to use '{sproc}', stored procedure sharing is not supported. Specify different names for the corresponding stored procedures.","messagePattern":"Both entity type '(.+?)' and '(.+?)' were configured to use '(.+?)', stored procedure sharing is not supported\\. Specify different names for the corresponding stored procedures\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":553,"sourceCode":"        IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)\n        => ValidateStoredProcedureCompatibility(mappedTypes, storedProcedure, logger);\n\n    /// <summary>\n    ///     Validates that a stored procedure is not shared across unrelated entity types.\n    /// </summary>\n    /// <param name=\"mappedTypes\">The entity types mapped to the stored procedure.</param>\n    /// <param name=\"storedProcedure\">The stored procedure identifier.</param>\n    /// <param name=\"logger\">The logger to use.</param>\n    protected virtual void ValidateStoredProcedureCompatibility(\n        IReadOnlyList<IEntityType> mappedTypes,\n        in StoreObjectIdentifier storedProcedure,\n        IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)\n    {\n        foreach (var mappedType in mappedTypes)\n        {\n            if (mappedTypes[0].GetRootType() != mappedType.GetRootType())\n            {\n                throw new InvalidOperationException(\n                    RelationalStrings.StoredProcedureTableSharing(\n                        mappedTypes[0].DisplayName(),\n                        mappedType.DisplayName(),\n                        storedProcedure.DisplayName()));\n            }\n        }\n    }\n\n    private static void ValidateSproc(\n        IStoredProcedure sproc,\n        string mappingStrategy,\n        IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)\n    {\n        var entityType = sproc.EntityType;\n        var storeObjectIdentifier = sproc.GetStoreIdentifier();\n\n        var primaryKey = entityType.FindPrimaryKey();\n        if (primaryKey == null)","sourceCodeStart":535,"sourceCodeEnd":571,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L535-L571","documentation":"Thrown in ValidateStoredProcedureCompatibility when two or more entity types are mapped to the same StoreObjectIdentifier (same sproc name/schema) but do not share a root type (RelationalModelValidator.cs:549-559). Stored-procedure sharing is only permitted among types in the same hierarchy; unrelated types must use distinct sproc names.","triggerScenarios":"Two unrelated entities both configured with .InsertUsingStoredProcedure(\"dbo.SaveThing\", ...). Or scaffolding generated the same default sproc name for entities whose names collided after a rename.","commonSituations":"Sharing a generic 'Upsert' sproc across many entities; copy-paste of sproc config blocks without renaming; TPC mapping where multiple concrete types accidentally land on the same store sproc identifier.","solutions":["Give each entity its own stored procedure name (.HasName(\"CustomerInsert\") vs \"OrderInsert\").","If the entities are genuinely the same hierarchy root, fix the model so they share GetRootType() (verify inheritance and mapping strategy).","For shared upsert logic, map separate thin wrapper sprocs per entity that delegate to the shared one server-side."],"exampleFix":"// before\nmodelBuilder.Entity<Customer>().InsertUsingStoredProcedure(\"dbo.Save\", sp => { /*...*/ });\nmodelBuilder.Entity<Order>().InsertUsingStoredProcedure(\"dbo.Save\", sp => { /*...*/ });\n\n// after — distinct names per entity\nmodelBuilder.Entity<Customer>().InsertUsingStoredProcedure(\"dbo.CustomerSave\", sp => { /*...*/ });\nmodelBuilder.Entity<Order>().InsertUsingStoredProcedure(\"dbo.OrderSave\", sp => { /*...*/ });","handlingStrategy":"validation","validationCode":"var byStore = new Dictionary<StoreObjectIdentifier, List<IEntityType>>();\nforeach (var et in modelBuilder.Model.GetEntityTypes())\n{\n    foreach (var (kind, sproc) in new (StoreObjectType, IStoredProcedure?)[]\n    {\n        (StoreObjectType.InsertStoredProcedure, et.GetInsertStoredProcedure()),\n        (StoreObjectType.UpdateStoredProcedure, et.GetUpdateStoredProcedure()),\n        (StoreObjectType.DeleteStoredProcedure, et.GetDeleteStoredProcedure()),\n    })\n    {\n        if (sproc is null) continue;\n        var id = sproc.GetStoreIdentifier();\n        if (!byStore.TryGetValue(id, out var list)) byStore[id] = list = new();\n        list.Add(et);\n        if (list.Select(e => e.GetRootType()).Distinct().Count() > 1)\n            throw new InvalidOperationException($\"Sproc '{id.DisplayName}' is shared across unrelated roots: {string.Join(\", \", list.Select(e => e.Name))}.\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try { _ = ctx.Model; }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"stored procedure sharing is not supported\"))\n{\n    logger.LogError(\"Each unrelated entity needs its own sproc name. {Msg}\", ex.Message);\n    throw;\n}","preventionTips":["Derive sproc names from the entity name (e.g., $\"{entity}Insert\") so collisions are unlikely.","Never share a single upsert sproc name across unrelated entities.","When reusing a DB sproc, create thin per-entity wrappers with distinct store names."],"tags":["efcore","relational","stored-procedure","mapping-conflict","entity-mapping","model-validation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}