dotnet/efcore · error · InvalidOperationException
Cannot create a 'SelectExpression' with a custom 'TableExpre
Error message
Cannot create a 'SelectExpression' with a custom 'TableExpressionBase' since the result type '{entityType}' is part of a hierarchy and does not contain a discriminator property. What it means
CreateSelect(IEntityType, TableExpressionBase) builds a SelectExpression over an externally-constructed table (FromSqlExpression, TableValuedFunctionExpression, ...). It refuses entity types that participate in an inheritance hierarchy but have no discriminator property, because EF needs a discriminator column to distinguish concrete types within the single composed SQL. TPT/TPC hierarchies or unmapped discriminators therefore cannot be used with FromSql*/TVF roots.
Source
Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.CreateSelect.cs:665
complexProperty.ClrType,
complexProperty.IsCollection);
return complexProperty.IsCollection
? new CollectionResultExpression(jsonQuery, complexProperty, elementType: complexType.ClrType)
: new RelationalStructuralTypeShaperExpression(complexType, jsonQuery, isNullable);
}
/// <summary>
/// Used to create a <see cref="SelectExpression" /> representing a query of the given entity type, when its table expression has
/// already been constructed externally. This overload is used for cases such as <see cref="FromSqlExpression" />,
/// <see cref="TableValuedFunctionExpression" />, etc.
/// </summary>
private SelectExpression CreateSelect(IEntityType entityType, TableExpressionBase tableExpressionBase)
{
if ((entityType.BaseType != null || entityType.GetDirectlyDerivedTypes().Any())
&& entityType.FindDiscriminatorProperty() == null)
{
throw new InvalidOperationException(RelationalStrings.SelectExpressionNonTphWithCustomTable(entityType.DisplayName()));
}
if (tableExpressionBase is not ITableBasedExpression { Table: ITableBase table })
{
throw new UnreachableException("SelectExpression with unexpected missing table");
}
var select = GenerateSingleTableSelect(entityType, table, tableExpressionBase);
AddEntitySelectConditions(select, entityType);
return select;
}
/***
* We need to add additional conditions on basic SelectExpression for certain cases
* - If we are selecting from TPH then we need to add condition for discriminator if mapping is incomplete
* - When we are selecting optional dependent sharing table, we need to add condition to figure out existence
* ** Optional Dependent **View on GitHub (pinned to 3a2006ef56)
Solutions
- Configure a discriminator on the hierarchy (TPH) so EF can distinguish types: modelBuilder.Entity<Base>().HasDiscriminator(b => b.Type).
- Avoid FromSql*/TVF roots on hierarchy roots; instead target a concrete (leaf) entity type that is not part of a hierarchy, or remap to TPH.
- If TPT/TPC is required, query through regular LINQ (not FromSql) so EF can emit per-table SELECTs.
Example fix
// before (hierarchy root, no discriminator)
var q = db.Set<BaseEntity>().FromSqlRaw("SELECT * FROM base_view").Where(b => b.Active);
// after (configure TPH discriminator)
modelBuilder.Entity<BaseEntity>()
.HasDiscriminator<string>("Discriminator")
.IsComplete();
var q = db.Set<BaseEntity>().FromSqlRaw("SELECT * FROM base_view").Where(b => b.Active); Defensive patterns
Strategy: validation
Validate before calling
// Before FromSql*/TVF over a hierarchy, ensure a discriminator exists.
var et = db.Model.FindEntityType(typeof(BaseEntity))!;
bool inHierarchy = et.BaseType is not null || et.GetDirectlyDerivedTypes().Any();
bool hasDiscriminator = et.FindDiscriminatorProperty() is not null;
if (inHierarchy && !hasDiscriminator)
throw new InvalidOperationException("Configure a TPH discriminator before using FromSql/TVF on this hierarchy."); Prevention
- Use FromSql*/TVF only on TPH hierarchies with a discriminator configured.
- Avoid FromSql* on TPT/TPC roots; query through regular LINQ instead.
- Add an integration test that exercises FromSql on every hierarchy root in the model.
When it happens
Trigger: context.Set<BaseEntity>().FromSqlRaw("SELECT * FROM vw_Base") where BaseEntity has derived types but no discriminator configured; using a TVF (HasDbFunction -> HasTranslation returning a TableValuedFunctionExpression) over a hierarchy root without a discriminator; calling FromSqlInterpolated on a TPT-mapped hierarchy root.
Common situations: Migrating a query to FromSql/TVF on an entity that was later added to an inheritance hierarchy; TPT/TPC hierarchies where discriminators are not configured; view-backed entities participating in inheritance.
Related errors
- Using '{methodName}' on DbSet of '{entityType}' is not suppo
- The element type of the result of '{dbFunction}' is mapped t
- The entity type '{entityType}' is mapped to a SQL query, but
- The entity type '{entityType}' is mapped to the 'DbFunction'
- The mapping strategy '{mappingStrategy}' specified on '{enti
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/d6020da0885eccd7.
Report an issue: GitHub.