{"record":{"id":"8b4c7347a7ce1ea8","repo":"dotnet/efcore","slug":"the-element-type-of-the-result-of-dbfunction-i","errorCode":null,"errorMessage":"The element type of the result of '{dbFunction}' is mapped to '{entityType}'. This is not supported since '{entityType}' is part of hierarchy but does not contain a discriminator property. Only TPH hierarchies can be mapped to a TVF.","messagePattern":"The element type of the result of '(.+?)' is mapped to '(.+?)'\\. This is not supported since '(.+?)' is part of hierarchy but does not contain a discriminator property\\. Only TPH hierarchies can be mapped to a TVF\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":397,"sourceCode":"                || ((IConventionModel)model).IsOwned(elementType)\n                || (entityType == null && model.GetEntityTypes().Any(e => e.ClrType == elementType)))\n            {\n                throw new InvalidOperationException(\n                    RelationalStrings.DbFunctionInvalidIQueryableOwnedReturnType(\n                        dbFunction.ModelName, elementType.ShortDisplayName()));\n            }\n\n            if (entityType == null)\n            {\n                throw new InvalidOperationException(\n                    RelationalStrings.DbFunctionInvalidReturnEntityType(\n                        dbFunction.ModelName, dbFunction.ReturnType.ShortDisplayName(), elementType.ShortDisplayName()));\n            }\n\n            if ((entityType.BaseType != null || entityType.GetDerivedTypes().Any())\n                && entityType.FindDiscriminatorProperty() == null)\n            {\n                throw new InvalidOperationException(\n                    RelationalStrings.TableValuedFunctionNonTph(dbFunction.ModelName, entityType.DisplayName()));\n            }\n        }\n\n        foreach (var parameter in dbFunction.Parameters)\n        {\n            if (parameter.TypeMapping == null)\n            {\n                throw new InvalidOperationException(\n                    RelationalStrings.DbFunctionInvalidParameterType(\n                        parameter.Name,\n                        dbFunction.ModelName,\n                        parameter.ClrType.ShortDisplayName()));\n            }\n        }\n    }\n\n    /// <summary>","sourceCodeStart":379,"sourceCodeEnd":415,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L379-L415","documentation":"Thrown when a DbFunction (TVF) returns an IQueryable<T> whose entity type is part of an inheritance hierarchy but has no discriminator property. EF only supports mapping a TVF to a TPH (table-per-hierarchy) hierarchy, because a TVF produces a single rowset. The check at RelationalModelValidator.cs:394-399 requires FindDiscriminatorProperty() to be non-null whenever the type has a BaseType or derived types.","triggerScenarios":"Defining a DbFunction returning IQueryable<BaseEntity> where BaseEntity is abstract or has derived types, while the hierarchy is mapped TPT or TPC (no discriminator). Also triggered if you forgot HasDiscriminator() on a base type that should be TPH.","commonSituations":"Switching an inheritance hierarchy from TPH to TPT/Tpc via UseTptMappingStrategy()/UseTpcMappingStrategy() without removing existing TVF mappings; using TVFs on hierarchies introduced after EF7's TPT/Tpc support; remapping an owned hierarchy.","solutions":["Remove the DbFunction/TVF mapping for hierarchies that are not TPH — TVFs require TPH.","If TPH is acceptable, configure the hierarchy as TPH and add a discriminator: modelBuilder.Entity<Base>().HasDiscriminator<string>(\"Discriminator\").HasValue<Base>(\"base\").HasValue<Derived>(\"derived\").","Split the TVF into per-type queries (FromSqlRaw / regular LINQ) instead of a single IQueryable<Base> TVF.","Make the TVF return a flat keyless DTO entity outside the hierarchy and project into it."],"exampleFix":"// before — hierarchy is TPT, no discriminator\nmodelBuilder.Entity<Person>().UseTptMappingStrategy();\nmodelBuilder.Entity<Employee> : Person\npublic IQueryable<Person> ActivePeople()\n    => FromExpression(() => ActivePeople());\n// throws TableValuedFunctionNonTph\n\n// after — TVF over TPH hierarchy\nmodelBuilder.Entity<Person>()\n    .HasDiscriminator<string>(\"PersonType\")\n    .HasValue<Customer>(\"customer\")\n    .HasValue<Employee>(\"employee\");","handlingStrategy":"validation","validationCode":"// Verify any TVF return type, if in a hierarchy, has a discriminator.\nforeach (var dbFn in modelBuilder.Model.GetDbFunctions())\n{\n    if (dbFn.IsScalar) continue;\n    var elementType = dbFn.ReturnType.GetGenericArguments()[0];\n    var et = modelBuilder.Model.FindEntityType(elementType);\n    if (et is null) continue;\n    if ((et.BaseType is not null || et.GetDerivedTypes().Any())\n        && et.FindDiscriminatorProperty() is null)\n    {\n        throw new InvalidOperationException($\"Hierarchy root '{et.Name}' used by TVF '{dbFn.Name}' must be TPH with a discriminator.\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try { _ = ctx.Model; }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Only TPH hierarchies can be mapped to a TVF\"))\n{\n    logger.LogError(\"TVF mapping requires TPH; switch hierarchy or remove TVF. {Msg}\", ex.Message);\n    throw;\n}","preventionTips":["When adopting TPT/Tpc, audit existing DbFunction/TVF mappings and remove or convert them.","Add HasDiscriminator(...) whenever an entity type participates in a hierarchy used by a TVF.","Document inheritance strategy per hierarchy in the entity's XML comments."],"tags":["efcore","relational","dbfunction","tvf","inheritance","tph","tpt","tpc","discriminator","model-validation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}