{"record":{"id":"e63649aab6e86d07","repo":"dotnet/efcore","slug":"the-mapping-strategy-mappingstrategy-used-for","errorCode":null,"errorMessage":"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.","messagePattern":"The mapping strategy '(.+?)' used for '(.+?)' is not supported for keyless entity types\\.  See https://go\\.microsoft\\.com/fwlink/\\?linkid=2130430 for more information\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":2122,"sourceCode":"                    RelationalStrings.NonTphMappingStrategy(mappingStrategy, entityType.DisplayName()));\n            }\n\n            ValidateTphMapping(entityType, StoreObjectType.Table);\n            ValidateTphMapping(entityType, StoreObjectType.View);\n            ValidateTphMapping(entityType, StoreObjectType.Function);\n            ValidateTphMapping(entityType, StoreObjectType.InsertStoredProcedure);\n            ValidateTphMapping(entityType, StoreObjectType.DeleteStoredProcedure);\n            ValidateTphMapping(entityType, StoreObjectType.UpdateStoredProcedure);\n\n            ValidateDiscriminatorValues(entityType);\n        }\n        else\n        {\n            if (mappingStrategy != RelationalAnnotationNames.TpcMappingStrategy\n                && entityType.FindPrimaryKey() == null\n                && entityType.GetDirectlyDerivedTypes().Any())\n            {\n                throw new InvalidOperationException(\n                    RelationalStrings.KeylessMappingStrategy(\n                        mappingStrategy ?? RelationalAnnotationNames.TptMappingStrategy, entityType.DisplayName()));\n            }\n\n            ValidateNonTphMapping(entityType, StoreObjectType.Table);\n            ValidateNonTphMapping(entityType, StoreObjectType.View);\n            ValidateNonTphMapping(entityType, StoreObjectType.InsertStoredProcedure);\n            ValidateNonTphMapping(entityType, StoreObjectType.DeleteStoredProcedure);\n            ValidateNonTphMapping(entityType, StoreObjectType.UpdateStoredProcedure);\n\n            var derivedTypes = entityType.GetDerivedTypesInclusive().ToList();\n            var discriminatorValues = new Dictionary<string, IEntityType>();\n            foreach (var derivedType in derivedTypes)\n            {\n                foreach (var complexProperty in derivedType.GetDeclaredComplexProperties())\n                {\n                    ValidateDiscriminatorValues(complexProperty.ComplexType);\n                }","sourceCodeStart":2104,"sourceCodeEnd":2140,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L2104-L2140","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nmodelBuilder.Entity<ViewBase>().HasNoKey().UseTptMappingStrategy();\npublic class ViewDerived : ViewBase { }\n// after (add a key so TPT is valid)\nmodelBuilder.Entity<ViewBase>().HasKey(x => x.Id).UseTptMappingStrategy();\n// or remove the derived type / strategy if the type must remain keyless","handlingStrategy":"validation","validationCode":"using (var ctx = new MyContext()) { ctx.Model.ToDebugString(); }\n// Assert no keyless type with derived types uses a non-TPC strategy.\nforeach (var et in ctx.Model.GetEntityTypes().Where(e => e.BaseType == null && e.GetDirectlyDerivedTypes().Any()))\n{\n    var s = (string?)et[Microsoft.EntityFrameworkCore.Metadata.RelationalAnnotationNames.MappingStrategy];\n    if (et.FindPrimaryKey() == null && s != \"TPC\")\n        Debug.Fail($\"Keyless {et.Name} has derived types but uses '{s ?? \"TPT(default)\"}; define a key or remove derived types.\");\n}","typeGuard":null,"tryCatchPattern":"try { using var ctx = new MyContext(); _ = ctx.Model; }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"not supported for keyless entity types\"))\n{ log.Error(\"Keyless type with hierarchy under TPT; add a key or flatten: {Msg}\", ex.Message); throw; }","preventionTips":["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."],"tags":["ef-core","model-validation","keyless","inheritance","mapping-strategy","tpt"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}