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
- 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.
- 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).
- 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
- Register the result entity type before calling HasDbFunction for any TVF.
- Avoid using DTOs as TVF return types; map a real entity with a key.
- Add a test that asserts every TVF's IQueryable<T> element type resolves to a mapped entity.
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
- Custom translation cannot be set on the DbFunction
- 'IsNullable' cannot be set on DbFunction
- 'PropagatesNullability' cannot be set on parameter
- The DbFunction ' ' defined on type ' ' must be either a…
- The DbFunction ' ' does not have a parameter named ' '.
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)