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
- Define a primary key on the entity (HasKey or conventions) if it should participate in TPT.
- Switch to a strategy compatible with keyless types, or remove the derived types so the hierarchy is flat.
- 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
- Keyless types (HasNoKey, query types) should not form TPT hierarchies; either add a key or keep them flat.
- When adding a derived class to a keyless base, review whether the base needs a key.
- Cover the keyless-vs-strategy invariant in a startup model-build test.
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
- The derived entity type '{entityType}' was configured with t
- The mapping strategy '{mappingStrategy}' specified on '{enti
- The specified discriminator value '{value}' for '{entityType
- The short name for '{entityType1}' is '{discriminatorValue}'
- The mapping strategy '{mappingStrategy}' specified on '{enti
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/e63649aab6e86d07.
Report an issue: GitHub.