dotnet/efcore · error · InvalidOperationException
Both entity type ' ' and ' ' were configured to use ' '…
Error message
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. What it means
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.
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.
Example fix
// before
modelBuilder.Entity<Customer>().InsertUsingStoredProcedure("dbo.Save", sp => { /*...*/ });
modelBuilder.Entity<Order>().InsertUsingStoredProcedure("dbo.Save", sp => { /*...*/ });
// after — distinct names per entity
modelBuilder.Entity<Customer>().InsertUsingStoredProcedure("dbo.CustomerSave", sp => { /*...*/ });
modelBuilder.Entity<Order>().InsertUsingStoredProcedure("dbo.OrderSave", sp => { /*...*/ }); Defensive patterns
Strategy: validation
Validate before calling
var byStore = new Dictionary<StoreObjectIdentifier, List<IEntityType>>();
foreach (var et in modelBuilder.Model.GetEntityTypes())
{
foreach (var (kind, sproc) in new (StoreObjectType, IStoredProcedure?)[]
{
(StoreObjectType.InsertStoredProcedure, et.GetInsertStoredProcedure()),
(StoreObjectType.UpdateStoredProcedure, et.GetUpdateStoredProcedure()),
(StoreObjectType.DeleteStoredProcedure, et.GetDeleteStoredProcedure()),
})
{
if (sproc is null) continue;
var id = sproc.GetStoreIdentifier();
if (!byStore.TryGetValue(id, out var list)) byStore[id] = list = new();
list.Add(et);
if (list.Select(e => e.GetRootType()).Distinct().Count() > 1)
throw new InvalidOperationException($"Sproc '{id.DisplayName}' is shared across unrelated roots: {string.Join(", ", list.Select(e => e.Name))}.");
}
} Try / catch
try { _ = ctx.Model; }
catch (InvalidOperationException ex) when (ex.Message.Contains("stored procedure sharing is not supported"))
{
logger.LogError("Each unrelated entity needs its own sproc name. {Msg}", ex.Message);
throw;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- The entity type ' ' was configured to use some stored…
- The entity type ' ' was configured to use ' ', but the…
- The keyless entity type
- Current value parameter
- Input parameter ' ' of insert stored procedure ' ' is…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/afb5991ff96a4b80.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:553
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
=> ValidateStoredProcedureCompatibility(mappedTypes, storedProcedure, logger);
/// <summary>
/// Validates that a stored procedure is not shared across unrelated entity types.
/// </summary>
/// <param name="mappedTypes">The entity types mapped to the stored procedure.</param>
/// <param name="storedProcedure">The stored procedure identifier.</param>
/// <param name="logger">The logger to use.</param>
protected virtual void ValidateStoredProcedureCompatibility(
IReadOnlyList<IEntityType> mappedTypes,
in StoreObjectIdentifier storedProcedure,
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
{
foreach (var mappedType in mappedTypes)
{
if (mappedTypes[0].GetRootType() != mappedType.GetRootType())
{
throw new InvalidOperationException(
RelationalStrings.StoredProcedureTableSharing(
mappedTypes[0].DisplayName(),
mappedType.DisplayName(),
storedProcedure.DisplayName()));
}
}
}
private static void ValidateSproc(
IStoredProcedure sproc,
string mappingStrategy,
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
{
var entityType = sproc.EntityType;
var storeObjectIdentifier = sproc.GetStoreIdentifier();
var primaryKey = entityType.FindPrimaryKey();
if (primaryKey == null)View on GitHub (pinned to 3a2006ef56)