dotnet/efcore · error · InvalidOperationException
The keyless entity type
Error message
The keyless entity type '{entityType}' was configured to use '{sproc}'. An entity type requires a primary key to be able to be mapped to a stored procedure. What it means
Thrown at the start of ValidateSproc when the entity type has no primary key (FindPrimaryKey() returns null) but is mapped to a stored procedure (RelationalModelValidator.cs:570-576). Stored procedures require a PK so EF can populate key parameters and result columns for identity/computed values. Keyless types (HasNoKey, query types, many-to-many join payloads without PK) cannot use sproc mapping.
Solutions
- Define a primary key on the entity: modelBuilder.Entity<T>().HasKey(x => x.Id); (or via [Key] / KeyAttribute).
- Remove the stored-procedure configuration from genuinely keyless types — keyless types are read-only by design.
- If the type was wrongly marked HasNoKey, remove that call so the convention picks up the key.
Example fix
// before
modelBuilder.Entity<SearchIndex>()
.HasNoKey()
.ToView("vw_Search")
.InsertUsingStoredProcedure("sp_SearchUpsert", sp => sp.HasParameter(s => s.Term));
// after — give it a key, or drop the sproc
modelBuilder.Entity<SearchIndex>()
.HasKey(s => s.Term);
modelBuilder.Entity<SearchIndex>()
.ToTable("Search")
.InsertUsingStoredProcedure("sp_SearchUpsert", sp => sp.HasParameter(s => s.Term)); Defensive patterns
Strategy: validation
Validate before calling
foreach (var et in modelBuilder.Model.GetEntityTypes())
{
bool anySproc = et.GetInsertStoredProcedure() is not null
|| et.GetUpdateStoredProcedure() is not null
|| et.GetDeleteStoredProcedure() is not null;
if (anySproc && et.FindPrimaryKey() is null)
throw new InvalidOperationException($"Entity '{et.Name}' has sproc mapping but no primary key; declare a key or remove the sproc config.");
} Try / catch
try { _ = ctx.Model; }
catch (InvalidOperationException ex) when (ex.Message.Contains("requires a primary key"))
{
logger.LogError("Keyless entity cannot use sproc mapping. {Msg}", ex.Message);
throw;
} Prevention
- Define HasKey(...) before adding any *UsingStoredProcedure configuration.
- Do not call HasNoKey() on entities you intend to persist via sprocs.
- Audit scaffolding output: views imported as keyless should not get CUD sproc config.
When it happens
Trigger: Configuring .InsertUsingStoredProcedure on a HasNoKey() entity; on a keyless query type; on a view-backed read model. Also on a type whose key was removed via .HasNoKey() after a refactor.
Common situations: Migrating a query type to an entity but forgetting to declare a key; scaffolding a view as keyless and then trying to add CUD sprocs; misapplying HasNoKey on a real entity.
Related errors
- Both entity type ' ' and ' ' were configured to use ' '…
- The entity type ' ' was configured to use some stored…
- The entity type ' ' was configured to use ' ', but the…
- Current value parameter
- Input parameter ' ' of insert stored procedure ' ' is…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/30ec55e0d6c6c7ab.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:573
mappedTypes[0].DisplayName(),
mappedType.DisplayName(),
storedProcedure.DisplayName()));
}
}
}
private static void ValidateSproc(
IStoredProcedure sproc,
string mappingStrategy,
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
{
var entityType = sproc.EntityType;
var storeObjectIdentifier = sproc.GetStoreIdentifier();
var primaryKey = entityType.FindPrimaryKey();
if (primaryKey == null)
{
throw new InvalidOperationException(
RelationalStrings.StoredProcedureKeyless(
entityType.DisplayName(), storeObjectIdentifier.DisplayName()));
}
var properties = entityType.GetDeclaredProperties().ToDictionary(p => p.Name);
if (mappingStrategy == RelationalAnnotationNames.TphMappingStrategy)
{
if (entityType.BaseType != null)
{
return;
}
foreach (var property in entityType.GetDerivedProperties())
{
properties.Add(property.Name, property);
}
}
else if (mappingStrategy == RelationalAnnotationNames.TpcMappingStrategy)View on GitHub (pinned to 3a2006ef56)