dotnet/efcore · error · InvalidOperationException
The mapping strategy
Error message
The mapping strategy '{mappingStrategy}' specified on '{entityType}' is not supported for entity types with a discriminator. What it means
Thrown by RelationalModelValidator.ValidateMappingStrategy when an entity type has a discriminator property configured (which implies TPH mapping) but a non-TPH mapping strategy (TPT or TPC) was set on it. A discriminator column only makes sense when the whole hierarchy shares one table, so combining a discriminator with TPT/TPC is contradictory and the model is rejected.
Solutions
- Remove the discriminator configuration (HasDiscriminator/HasDiscriminatorProperty) from the entity types when using UseTptMappingStrategy() or UseTpcMappingStrategy().
- If you want the discriminator, switch the strategy back to TPH: modelBuilder.UseTphMappingStrategy() (the default).
- Check for any [Discriminator] attributes or fluent HasDiscriminator calls on base and derived types and delete them.
Example fix
// before
modelBuilder.Entity<Base>().HasDiscriminator<string>("Discriminator");
modelBuilder.UseTptMappingStrategy(); // throws 420
// after (TPT - remove discriminator)
modelBuilder.UseTptMappingStrategy();
// no HasDiscriminator call Defensive patterns
Strategy: validation
Validate before calling
// Run before building/finalizing the model (e.g. in OnModelCreating or a unit test)
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var hasDiscriminator = entityType.FindDiscriminatorProperty() != null;
var strategy = entityType.GetMappingStrategy();
if (hasDiscriminator
&& strategy is not null
&& strategy != RelationalAnnotationNames.TphMappingStrategy)
{
throw new InvalidOperationException($"{entityType.DisplayName()} has a discriminator but uses {strategy}; remove the discriminator or switch to TPH.");
}
} Prevention
- When switching a hierarchy to TPT/TPC, search the configuration for HasDiscriminator and remove all such calls.
- Prefer the UseTph/Tpt/TpcMappingStrategy() fluent API over hand-written annotations so the strategy and discriminator stay consistent.
- Add a model-validation unit test that calls context.Model.ToString() or context.Database.EnsureCreated() in a test to fail fast at build time.
When it happens
Trigger: An entity is configured with both HasDiscriminator()/HasDiscriminatorProperty() (or a discriminator property is otherwise detected via FindDiscriminatorProperty() != null) and modelBuilder.UseTptMappingStrategy() or UseTpcMappingStrategy() — or the strategy annotation is set on the root while a discriminator is still present.
Common situations: Migrating an existing TPH hierarchy to TPT/TPC but forgetting to remove the discriminator configuration; copy-pasting discriminator setup from a base entity while switching the hierarchy strategy; upgrading from older EF Core where discriminator + strategy coexisted differently.
Related errors
- The mapping strategy
- The short name for ' ' is ' ' which is the same for ' '…
- The specified discriminator value
- Both ' ' and ' ' are mapped to the table ' '. All the…
- Both ' ' and ' ' are mapped to the stored procedure ' '…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/fd16d1693f574d94.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:2103
if (entityType.BaseType != null)
{
if (mappingStrategy != null
&& mappingStrategy != (string?)entityType.BaseType[RelationalAnnotationNames.MappingStrategy])
{
throw new InvalidOperationException(
RelationalStrings.DerivedStrategy(entityType.DisplayName(), mappingStrategy));
}
return;
}
// Hierarchy mapping strategy must be the same across all types of mappings (only for root types)
if (entityType.FindDiscriminatorProperty() != null)
{
if (mappingStrategy is not null
and not RelationalAnnotationNames.TphMappingStrategy)
{
throw new InvalidOperationException(
RelationalStrings.NonTphMappingStrategy(mappingStrategy, entityType.DisplayName()));
}
ValidateTphMapping(entityType, StoreObjectType.Table);
ValidateTphMapping(entityType, StoreObjectType.View);
ValidateTphMapping(entityType, StoreObjectType.Function);
ValidateTphMapping(entityType, StoreObjectType.InsertStoredProcedure);
ValidateTphMapping(entityType, StoreObjectType.DeleteStoredProcedure);
ValidateTphMapping(entityType, StoreObjectType.UpdateStoredProcedure);
ValidateDiscriminatorValues(entityType);
}
else
{
if (mappingStrategy != RelationalAnnotationNames.TpcMappingStrategy
&& entityType.FindPrimaryKey() == null
&& entityType.GetDirectlyDerivedTypes().Any())
{View on GitHub (pinned to 3a2006ef56)