dotnet/efcore · error · InvalidOperationException
The mapping strategy
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
Thrown during hierarchy validation when an entity type has no primary key (is keyless), has one or more derived types, and the hierarchy is not using the TPC mapping strategy. TPT/TPH inheritance requires each type to have a primary key so the rows can be linked/resolved, so a keyless type in a non-TPC hierarchy is illegal. Note the message defaults the strategy to TPT when it is null.
Solutions
- Define a primary key on the entity with HasKey(...) or a [Key] attribute.
- If the type must stay keyless, use TPC: modelBuilder.UseTpcMappingStrategy() — the only strategy that tolerates keyless types in a hierarchy.
- Remove the inheritance relationship (make the derived types standalone) if a key is genuinely impossible.
Example fix
// before modelBuilder.Entity<KeylessBase>().HasNoKey(); modelBuilder.Entity<Derived : KeylessBase>(); // default TPH strategy -> throws 421 // after (add a key) modelBuilder.Entity<KeylessBase>().HasKey(x => x.Id); // or use TPC // modelBuilder.UseTpcMappingStrategy();
Defensive patterns
Strategy: validation
Validate before calling
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var strategy = entityType.GetMappingStrategy();
var isKeyless = entityType.FindPrimaryKey() == null;
var hasDerived = entityType.GetDirectlyDerivedTypes().Any();
if (isKeyless && hasDerived
&& strategy != RelationalAnnotationNames.TpcMappingStrategy)
{
throw new InvalidOperationException($"{entityType.DisplayName()} is keyless with derived types; add a key or use TPC.");
}
} Prevention
- Never use HasNoKey() on a base type of an inheritance hierarchy unless the hierarchy is TPC.
- Run a model-build test that catches keyless+inheritance misconfigurations before deployment.
- Audit [Key]/HasKey usage whenever introducing inheritance.
When it happens
Trigger: A keyless entity type (HasNoKey) that is the base of an inheritance hierarchy while the mapping strategy is TPH (default) or TPT, i.e. FindPrimaryKey() == null && GetDirectlyDerivedTypes().Any() && strategy != TPC.
Common situations: Using a keyless query result type as a base for other types; forgetting a [Key] or HasKey on the base of a hierarchy; mixing owned/keyless concepts with inheritance; switching a hierarchy to include a keyless view-backed type.
Related errors
- Both ' ' and ' ' are mapped to the table ' '. All the…
- Both ' ' and ' ' are mapped to the stored procedure ' '…
- Both ' ' and ' ' are mapped to the view ' '. All the entity…
- The entity type ' ' is not mapped, so by default the owned…
- The mapping strategy
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/e63649aab6e86d07.
Report an issue: GitHub.
Appendix: 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 3a2006ef56)