{"record":{"id":"30ec55e0d6c6c7ab","repo":"dotnet/efcore","slug":"the-keyless-entity-type-entitytype-was-configu","errorCode":null,"errorMessage":"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.","messagePattern":"The keyless entity type '(.+?)' was configured to use '(.+?)'\\. An entity type requires a primary key to be able to be mapped to a stored procedure\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":573,"sourceCode":"                        mappedTypes[0].DisplayName(),\n                        mappedType.DisplayName(),\n                        storedProcedure.DisplayName()));\n            }\n        }\n    }\n\n    private static void ValidateSproc(\n        IStoredProcedure sproc,\n        string mappingStrategy,\n        IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)\n    {\n        var entityType = sproc.EntityType;\n        var storeObjectIdentifier = sproc.GetStoreIdentifier();\n\n        var primaryKey = entityType.FindPrimaryKey();\n        if (primaryKey == null)\n        {\n            throw new InvalidOperationException(\n                RelationalStrings.StoredProcedureKeyless(\n                    entityType.DisplayName(), storeObjectIdentifier.DisplayName()));\n        }\n\n        var properties = entityType.GetDeclaredProperties().ToDictionary(p => p.Name);\n        if (mappingStrategy == RelationalAnnotationNames.TphMappingStrategy)\n        {\n            if (entityType.BaseType != null)\n            {\n                return;\n            }\n\n            foreach (var property in entityType.GetDerivedProperties())\n            {\n                properties.Add(property.Name, property);\n            }\n        }\n        else if (mappingStrategy == RelationalAnnotationNames.TpcMappingStrategy)","sourceCodeStart":555,"sourceCodeEnd":591,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L555-L591","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nmodelBuilder.Entity<SearchIndex>()\n    .HasNoKey()\n    .ToView(\"vw_Search\")\n    .InsertUsingStoredProcedure(\"sp_SearchUpsert\", sp => sp.HasParameter(s => s.Term));\n\n// after — give it a key, or drop the sproc\nmodelBuilder.Entity<SearchIndex>()\n    .HasKey(s => s.Term);\nmodelBuilder.Entity<SearchIndex>()\n    .ToTable(\"Search\")\n    .InsertUsingStoredProcedure(\"sp_SearchUpsert\", sp => sp.HasParameter(s => s.Term));","handlingStrategy":"validation","validationCode":"foreach (var et in modelBuilder.Model.GetEntityTypes())\n{\n    bool anySproc = et.GetInsertStoredProcedure() is not null\n                 || et.GetUpdateStoredProcedure() is not null\n                 || et.GetDeleteStoredProcedure() is not null;\n    if (anySproc && et.FindPrimaryKey() is null)\n        throw new InvalidOperationException($\"Entity '{et.Name}' has sproc mapping but no primary key; declare a key or remove the sproc config.\");\n}","typeGuard":null,"tryCatchPattern":"try { _ = ctx.Model; }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"requires a primary key\"))\n{\n    logger.LogError(\"Keyless entity cannot use sproc mapping. {Msg}\", ex.Message);\n    throw;\n}","preventionTips":["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."],"tags":["efcore","relational","stored-procedure","keyless","primary-key","entity-mapping","model-validation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}