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

  1. Configure a discriminator on the hierarchy (TPH) so EF can distinguish types: modelBuilder.Entity<Base>().HasDiscriminator(b => b.Type).
  2. 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.
  3. 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

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


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/d6020da0885eccd7. Report an issue: GitHub.