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

  1. Remove the discriminator configuration (HasDiscriminator/HasDiscriminatorProperty) from the entity types when using UseTptMappingStrategy() or UseTpcMappingStrategy().
  2. If you want the discriminator, switch the strategy back to TPH: modelBuilder.UseTphMappingStrategy() (the default).
  3. 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 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


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)