dotnet/efcore · error · InvalidOperationException
The DbFunction '{function}' has an invalid return type '{typ
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
Thrown by TableValuedDbFunctionConvention.ProcessModelFinalizing when a non-scalar (IQueryable-returning) DbFunction's element type is not a valid entity type. EF requires TVFs to return IQueryable<T> where T is (or can become) an entity type so it can shape results into tracked entities. The convention runs at model finalization, so the error surfaces late — when the model is being sealed.
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 dbf9771522)
Solutions
- Make the IQueryable<T>'s T a real entity type by configuring it in the model with a primary key (modelBuilder.Entity<T>().HasKey(...)) or HasNoKey() if it is a keyless entity.
- If the function returns scalar/primitive rows, change the return type to a scalar type that the provider can map instead of IQueryable<T>.
- If T is only used as a projection, introduce a dedicated entity/owned type or keyless entity for the row shape and map that.
- Verify the return type is exactly IQueryable<T> (not IEnumerable<T>, Task<IQueryable<T>>, or a custom wrapper).
Example fix
// before
public IQueryable<RawOrderRow> OrderRows(int id)
=> FromExpression(() => OrderRows(id));
// 'RawOrderRow' is not configured as an entity
// after
modelBuilder.Entity<RawOrderRow>(b =>
{
b.HasNoKey();
b.Property(r => r.OrderId);
b.Property(r => r.Total);
});
public IQueryable<RawOrderRow> OrderRows(int id)
=> FromExpression(() => OrderRows(id)); Defensive patterns
Strategy: validation
Validate before calling
// Before finalizing the model, verify each non-scalar DbFunction returns IQueryable<ValidEntityType>
foreach (var fn in modelBuilder.Model.GetDbFunctions())
{
if (!fn.IsScalar)
{
var elem = fn.ReturnType.TryGetElementType(typeof(IQueryable<>));
if (elem is null || !elem.IsValidEntityType())
{
throw new InvalidOperationException($"{fn.ModelName} must return IQueryable<T> of a valid entity type.");
}
}
} Type guard
// Reflect over a candidate method before registering as a TVF DbFunction
static bool IsValidTvfReturn(Type returnType, IMutableModel model)
{
var elem = returnType.TryGetElementType(typeof(IQueryable<>));
return elem is not null && elem.IsValidEntityType();
} Prevention
- Always configure the row type returned by a TVF as an entity (HasKey) or keyless entity (HasNoKey) in the same OnModelCreating.
- Write a unit test that builds the model and runs the first query in-memory to surface convention errors early.
- Keep TVF row types in their own entity classes; do not reuse arbitrary DTOs.
When it happens
Trigger: Registering a DbFunction whose method returns IQueryable<T> where T is a struct, an interface, a class with no key, or a type that cannot be mapped as an entity (e.g. IQueryable<string>, IQueryable<Dictionary>, or IQueryable<DtoWithoutKey>). Triggered via HasDbFunction on a DbContext or via modelBuilder.HasDbFunction(...).
Common situations: Developers try to map a TVF that returns a row type that is a plain DTO rather than an entity. Also happens after refactoring a return type from a scalar to a queryable of a non-entity, or when a keyless entity is expected but the type was never configured with HasNoKey or a key.
Related errors
- 'IsNullable' cannot be set on DbFunction '{functionName}' si
- Custom translation cannot be set on the DbFunction '{functio
- 'PropagatesNullability' cannot be set on parameter '{paramet
- The DbFunction '{function}' is generic. Mapping generic meth
- The DbFunction '{function}' defined on type '{type}' must be
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/f197af4a035f6e5f.
Report an issue: GitHub.