dotnet/efcore · error · InvalidOperationException

The mapping strategy '{mappingStrategy}' used for '{entityTy

Error message

The mapping strategy '{mappingStrategy}' used for '{entityType}' is not supported for keyless entity types.  See https://go.microsoft.com/fwlink/?linkid=2130430 for more information.

What it means

A non-TPC mapping strategy (TPT or default-TPT) is used on a keyless entity type (no primary key) that has derived types. TPT and (by extension) non-TPH strategies require a primary key to define the per-type tables and their joins; keyless types cannot support them. Thrown from ValidateInheritanceMapping when strategy != TPC, FindPrimaryKey() == null, and the type has directly derived types.

Source

Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:2122

                    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())
            {
                throw new InvalidOperationException(
                    RelationalStrings.KeylessMappingStrategy(
                        mappingStrategy ?? RelationalAnnotationNames.TptMappingStrategy, entityType.DisplayName()));
            }

            ValidateNonTphMapping(entityType, StoreObjectType.Table);
            ValidateNonTphMapping(entityType, StoreObjectType.View);
            ValidateNonTphMapping(entityType, StoreObjectType.InsertStoredProcedure);
            ValidateNonTphMapping(entityType, StoreObjectType.DeleteStoredProcedure);
            ValidateNonTphMapping(entityType, StoreObjectType.UpdateStoredProcedure);

            var derivedTypes = entityType.GetDerivedTypesInclusive().ToList();
            var discriminatorValues = new Dictionary<string, IEntityType>();
            foreach (var derivedType in derivedTypes)
            {
                foreach (var complexProperty in derivedType.GetDeclaredComplexProperties())
                {
                    ValidateDiscriminatorValues(complexProperty.ComplexType);
                }

View on GitHub (pinned to dbf9771522)

Solutions

  1. Define a primary key on the entity (HasKey or conventions) if it should participate in TPT.
  2. Switch to a strategy compatible with keyless types, or remove the derived types so the hierarchy is flat.
  3. If the type is genuinely keyless (view/query), do not use TPT/TPC for its hierarchy - keep it a single unmapped or TPH-style type.

Example fix

// before
modelBuilder.Entity<ViewBase>().HasNoKey().UseTptMappingStrategy();
public class ViewDerived : ViewBase { }
// after (add a key so TPT is valid)
modelBuilder.Entity<ViewBase>().HasKey(x => x.Id).UseTptMappingStrategy();
// or remove the derived type / strategy if the type must remain keyless
Defensive patterns

Strategy: validation

Validate before calling

using (var ctx = new MyContext()) { ctx.Model.ToDebugString(); }
// Assert no keyless type with derived types uses a non-TPC strategy.
foreach (var et in ctx.Model.GetEntityTypes().Where(e => e.BaseType == null && e.GetDirectlyDerivedTypes().Any()))
{
    var s = (string?)et[Microsoft.EntityFrameworkCore.Metadata.RelationalAnnotationNames.MappingStrategy];
    if (et.FindPrimaryKey() == null && s != "TPC")
        Debug.Fail($"Keyless {et.Name} has derived types but uses '{s ?? "TPT(default)"}; define a key or remove derived types.");
}

Try / catch

try { using var ctx = new MyContext(); _ = ctx.Model; }
catch (InvalidOperationException ex) when (ex.Message.Contains("not supported for keyless entity types"))
{ log.Error("Keyless type with hierarchy under TPT; add a key or flatten: {Msg}", ex.Message); throw; }

Prevention

When it happens

Trigger: Configuring modelBuilder.Entity<KeylessBase>().HasNoKey().UseTptMappingStrategy() where KeylessBase has derived types; a query type / view-mapped keyless entity that gained a derived type while a TPT strategy is set.

Common situations: Promoting a keyless view to have sub-types while keeping a TPT mapping; using a keyless base with derived entities accidentally introduced by adding an inheriting class; refactoring from a query root to an inheritance hierarchy without keys.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/e63649aab6e86d07. Report an issue: GitHub.