dotnet/efcore · error · InvalidOperationException

Using ' ' on DbSet of ' ' is not supported since ' ' is…

Error message

Using '{methodName}' on DbSet of '{entityType}' is not supported since '{entityType}' is part of hierarchy and does not contain a discriminator property.

What it means

GenerateFromSqlQueryRoot throws MethodOnNonTphRootNotSupported when FromSqlRaw/FromSqlInterpolated is called on a DbSet whose entity type participates in an inheritance hierarchy (has a base type or derived types) but has no discriminator property. FromSql can only target the TPH root that owns the discriminator so EF can filter rows; an inheritance branch without a discriminator cannot be the SQL root.

Solutions

  1. Call FromSqlRaw on the hierarchy root DbSet and add a WHERE clause filtering by discriminator/column values.
  2. If TPH, configure HasDiscriminator on the root entity so the check passes.
  3. For TPT/TPC, query the root and project, or use a plain SqlQueryRaw<T> for the leaf type instead of DbSet.FromSql.

Example fix

// before (DerivedEntity is in a hierarchy, no discriminator)
var rows = await db.Set<DerivedEntity>()
    .FromSqlRaw("SELECT * FROM derived_view").ToListAsync();

// after — query the root and filter
var rows = await db.Set<BaseEntity>()
    .FromSqlRaw("SELECT * FROM base_view WHERE Discriminator = 'DerivedEntity'")
    .OfType<DerivedEntity>().ToListAsync();
Defensive patterns

Strategy: validation

Validate before calling

var entityType = db.Model.FindEntityType(typeof(TEntity))!;
if ((entityType.BaseType != null || entityType.GetDirectlyDerivedTypes().Any())
    && entityType.FindDiscriminatorProperty() == null)
    throw new InvalidOperationException("Cannot FromSql on this hierarchy node; query the root.");

Type guard

static bool CanFromSql(IEntityType e)
    => e.BaseType == null && !e.GetDirectlyDerivedTypes().Any()
       || e.FindDiscriminatorProperty() is not null;

Try / catch

try { return await db.Set<T>().FromSqlRaw(sql).ToListAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("does not contain a discriminator"))
{
    // fall back to querying the root and filtering
    return await db.Set<TRoot>().FromSqlRaw(sql).OfType<T>().ToListAsync();
}

Prevention

When it happens

Trigger: Calling context.Set<DerivedEntity>().FromSqlRaw(...) where DerivedEntity is part of TPT/TPC inheritance, or TPH without a configured discriminator. The check is `entityType.BaseType != null || entityType.GetDirectlyDerivedTypes().Any()` combined with `FindDiscriminatorProperty() == null`.

Common situations: Switching from TPH to TPT/TPC and reusing existing FromSql calls; scaffolding a hierarchy without a discriminator column; forgetting HasDiscriminator on an abstract base; calling FromSql on a leaf entity in a hierarchy.

Related errors


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

Appendix: source

Thrown at src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs:192

        return queryableSource.Provider.CreateQuery<TEntity>(
            GenerateFromSqlQueryRoot(
                queryableSource,
                sql.Format,
                sql.GetArguments()));
    }

    private static FromSqlQueryRootExpression GenerateFromSqlQueryRoot(
        IQueryable source,
        string sql,
        object?[] arguments,
        [CallerMemberName] string memberName = null!)
    {
        var entityQueryRootExpression = (EntityQueryRootExpression)source.Expression;

        var entityType = entityQueryRootExpression.EntityType;
        return (entityType.BaseType != null || entityType.GetDirectlyDerivedTypes().Any())
            && entityType.FindDiscriminatorProperty() == null
                ? throw new InvalidOperationException(
                    RelationalStrings.MethodOnNonTphRootNotSupported(memberName, entityType.DisplayName()))
                : new FromSqlQueryRootExpression(
                    entityQueryRootExpression.QueryProvider!,
                    entityType,
                    sql,
                    Expression.Constant(arguments));
    }

    #endregion

    #region SplitQuery

    /// <summary>
    ///     Returns a new query which is configured to load the collections in the query results in a single database query.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         This behavior generally guarantees result consistency in the face of concurrent updates

View on GitHub (pinned to 3a2006ef56)