dotnet/efcore · error · InvalidOperationException
' ' is mapped to the stored procedure ' ' while ' ' is…
Error message
'{entityType}' is mapped to the stored procedure '{sproc}' while '{otherEntityType}' is mapped to the stored procedure '{otherSproc}'. Map all the entity types in the hierarchy to the same stored procedure, or remove the discriminator and map them all to different stored procedures. See https://go.microsoft.com/fwlink/?linkid=2130430 for more information. What it means
Thrown inside ValidateTphMapping when a derived type resolves to a different Insert/Delete/Update stored procedure StoreObjectIdentifier than the root. TPH requires the whole hierarchy to use the same stored procedure (defined on the root), so a derived type mapped to a different sproc is rejected.
Solutions
- Configure the stored procedure only on the root and include all derived-type parameters there.
- Switch the hierarchy to TPT/TPC if separate stored procedures per type are genuinely needed.
Example fix
// before (TPH, different sproc on derived)
modelBuilder.Entity<Person>().UpdateUsingStoredProcedure("sp_UpdatePerson");
modelBuilder.Entity<Customer>().UpdateUsingStoredProcedure("sp_UpdateCustomer"); // throws 435
// after (single sproc on root)
modelBuilder.Entity<Person>().UpdateUsingStoredProcedure("sp_UpdatePerson")
.HasParameter("ShippingAddress", p => ((Customer)p).ShippingAddress); Defensive patterns
Strategy: validation
Validate before calling
foreach (var root in modelBuilder.Model.GetEntityTypes().Where(t => t.BaseType == null))
{
if (root.FindDiscriminatorProperty() == null) continue;
foreach (var sot in new[] { StoreObjectType.InsertStoredProcedure, StoreObjectType.DeleteStoredProcedure, StoreObjectType.UpdateStoredProcedure })
{
var rootSo = StoreObjectIdentifier.Create(root, sot);
if (rootSo == null) continue;
foreach (var et in root.GetDerivedTypes())
{
var so = StoreObjectIdentifier.Create(et, sot);
if (so != null && so.Value != rootSo.Value)
throw new InvalidOperationException($"{et.DisplayName()} {sot} {so.Value.DisplayName()} differs from root {rootSo.Value.DisplayName()} (TPH).");
}
}
} Prevention
- In TPH, declare stored procedures on the root only.
- Switch to TPT/TPC if separate stored procedures per type are needed.
- Add a model test that verifies sproc consistency across a TPH hierarchy.
When it happens
Trigger: A TPH hierarchy where the root declares a *UsingStoredProcedure and a derived type resolves to a different stored procedure (different name/store object) rather than reusing the root's.
Common situations: Per-type stored procedure config accidentally applied to a derived type in TPH; partial TPT migration; copy-paste of sproc config across the hierarchy.
Related errors
- Both ' ' and ' ' are explicitly mapped to the stored…
- Both ' ' and ' ' are mapped to the stored procedure ' '…
- ' ' is mapped to the database function ' ' while ' ' is…
- ' ' is mapped to the table ' ' while ' ' is mapped to the…
- ' ' is mapped to the view ' ' while ' ' is mapped to the…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/66a086a5a5d7cff7.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:2347
case StoreObjectType.Table:
throw new InvalidOperationException(
RelationalStrings.TphTableMismatch(
entityType.DisplayName(), entityId.Value.DisplayName(),
rootEntityType.DisplayName(), rootId?.DisplayName()));
case StoreObjectType.View:
throw new InvalidOperationException(
RelationalStrings.TphViewMismatch(
entityType.DisplayName(), entityId.Value.DisplayName(),
rootEntityType.DisplayName(), rootId?.DisplayName()));
case StoreObjectType.Function:
throw new InvalidOperationException(
RelationalStrings.TphDbFunctionMismatch(
entityType.DisplayName(), entityId.Value.DisplayName(),
rootEntityType.DisplayName(), rootId?.DisplayName()));
case StoreObjectType.InsertStoredProcedure:
case StoreObjectType.DeleteStoredProcedure:
case StoreObjectType.UpdateStoredProcedure:
throw new InvalidOperationException(
RelationalStrings.TphStoredProcedureMismatch(
entityType.DisplayName(), entityId.Value.DisplayName(),
rootEntityType.DisplayName(), rootId?.DisplayName()));
}
}
}
/// <inheritdoc />
protected override bool IsRedundant(IForeignKey foreignKey)
=> base.IsRedundant(foreignKey)
&& !foreignKey.DeclaringEntityType.GetMappingFragments().Any();
/// <summary>
/// Validates the mapping fragments for an entity type.
/// </summary>
/// <param name="entityType">The entity type to validate.</param>
/// <param name="logger">The logger to use.</param>
protected virtual void ValidateMappingFragment(View on GitHub (pinned to 3a2006ef56)