dotnet/efcore · error · InvalidOperationException

The keyless entity type '{entityType}' was configured to use

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 when a keyless entity type (no primary key, HasNoKey) is mapped to a stored procedure. Stored procedures require a primary key to identify rows for insert/update/delete operations; the validator rejects a null primary key in ValidateSproc.

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 dbf9771522)

Solutions

  1. Define a primary key on the entity (HasKey) so it can be identified for sproc operations.
  2. Or remove the stored procedure mapping and keep the entity as a query/view (read-only).

Example fix

// before
modelBuilder.Entity<BlogStat>().HasNoKey().InsertStoredProcedure("sp_InsertStat");

// after
modelBuilder.Entity<BlogStat>().HasKey(x => x.Id).InsertStoredProcedure("sp_InsertStat");
Defensive patterns

Strategy: validation

Validate before calling

foreach (var et in modelBuilder.Model.GetEntityTypes())
{
    if (et.FindPrimaryKey() == null && (et.GetInsertStoredProcedure() != null || et.GetUpdateStoredProcedure() != null || et.GetDeleteStoredProcedure() != null))
        throw new InvalidOperationException($"Keyless {et.DisplayName()} cannot be mapped to sprocs");
}

Try / catch

try { _ = ctx.Model; } catch (InvalidOperationException ex) when (ex.Message.Contains("requires a primary key")) { /* add a key or remove the sproc mapping */ }

Prevention

When it happens

Trigger: Configuring Insert/Update/Delete sprocs on a query type or a HasNoKey() entity; converting a keyless view entity to sproc-based mapping.

Common situations: Treating a read-only/keyless projection as if it were writable; sproc config copied from a keyed entity onto a keyless one.

Related errors


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