dotnet/efcore · error · InvalidOperationException

The DbFunction ' ' has an invalid return type ' '…

Error message

The DbFunction '{function}' has an invalid return type '{type}'. Non-scalar functions must return 'IQueryable' of a valid entity type.

What it means

When a DbFunction is registered as a non-scalar (table-valued) function, EF Core requires its return type to be IQueryable<T> where T is a valid entity type mapped in the model. This throw from TableValuedDbFunctionConvention.ProcessDbFunctionAdded fires when the element type is not a recognized entity, so EF cannot map the TVF result rows.

Solutions

  1. Make the type used as the IQueryable<T> argument a real entity: call modelBuilder.Entity<T>() or otherwise include T in the model before registering the DbFunction.
  2. Ensure T is not an owned type unless it is also a valid principal entity (note the convention allows owned types only when explicitly registered — verify your scenario).
  3. If the function is actually scalar, change the return type to a scalar (non-IQueryable) so EF treats it as a scalar function instead.

Example fix

// before
public IQueryable<OrderSummary> GetOrderSummaries() => FromSqlInterpolated(...);
modelBuilder.HasDbFunction(typeof(MyContext).GetMethod(nameof(GetOrderSummaries)));
// OrderSummary is not registered as an entity

// after
modelBuilder.Entity<OrderSummary>(b => { /* key + mappings */ });
modelBuilder.HasDbFunction(typeof(MyContext).GetMethod(nameof(GetOrderSummaries)));
Defensive patterns

Strategy: validation

Validate before calling

// Validate the element type is a registered entity before registering the TVF
var elementType = typeof(OrderSummary);
if (!modelBuilder.Model.FindEntityType(elementType).IsKeyless
    && modelBuilder.Model.FindEntityType(elementType) is null)
{
    throw new InvalidOperationException($"{elementType.Name} must be registered as an entity before mapping the TVF.");
}
modelBuilder.HasDbFunction(typeof(MyContext).GetMethod(nameof(MyContext.GetOrderSummaries)));

Type guard

static bool IsMappableEntityTvfResult(Type t, IModel model)
    => model.FindEntityType(t) is { } et && !et.IsKeyless;

Prevention

When it happens

Trigger: Registering a method whose return type is IQueryable<T> where T is a non-entity class, a primitive, or an owned type that is not declared as an entity; calling HasDbFunction on a method returning IQueryable<object> or IQueryable<dynamic>.

Common situations: Adding a TVF mapping before declaring the result type as an entity; using a DTO as the TVF return type instead of a configured entity; switching a scalar function to a TVF and forgetting to register the return type.

Related errors


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

Appendix: source

Thrown at src/EFCore.Relational/Metadata/Conventions/TableValuedDbFunctionConvention.cs:65

    }

    /// <summary>
    ///     Called when an <see cref="IConventionDbFunction" /> is added to the model.
    /// </summary>
    /// <param name="dbFunctionBuilder">The builder for the <see cref="IConventionDbFunction" />.</param>
    private static void ProcessDbFunctionAdded(
        IConventionDbFunctionBuilder dbFunctionBuilder)
    {
        var function = dbFunctionBuilder.Metadata;
        if (function.IsScalar)
        {
            return;
        }

        var elementType = function.ReturnType.TryGetElementType(typeof(IQueryable<>))!;
        if (!elementType.IsValidEntityType())
        {
            throw new InvalidOperationException(
                RelationalStrings.DbFunctionInvalidIQueryableReturnType(
                    function.ModelName, function.ReturnType.ShortDisplayName()));
        }

        var model = function.Model;
        var entityType = model.FindEntityType(elementType);
        if (entityType?.IsOwned() == true
            || model.IsOwned(elementType)
            || (entityType == null && model.FindEntityTypes(elementType).Any()))
        {
            return;
        }

        dbFunctionBuilder.ModelBuilder.Entity(elementType);
    }
}

View on GitHub (pinned to 3a2006ef56)