dotnet/efcore · error · InvalidOperationException

Custom translation cannot be set on the DbFunction '{functio

Error message

Custom translation cannot be set on the DbFunction '{function}' since it is not a scalar function.

What it means

Thrown by DbFunction.SetTranslation when a custom translation delegate is set on a function that is not a scalar, non-aggregate function (i.e. IsScalar is false or IsAggregate is true). Custom translation is only supported for scalar functions whose arguments map to a single SqlExpression result.

Source

Thrown at src/EFCore.Relational/Metadata/Internal/DbFunction.cs:611

        set => SetTranslation(value, ConfigurationSource.Explicit);
    }

    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public virtual Func<IReadOnlyList<SqlExpression>, SqlExpression>? SetTranslation(
        Func<IReadOnlyList<SqlExpression>, SqlExpression>? translation,
        ConfigurationSource configurationSource)
    {
        EnsureMutable();

        if (translation != null
            && (!IsScalar || IsAggregate))
        {
            throw new InvalidOperationException(RelationalStrings.DbFunctionNonScalarCustomTranslation(MethodInfo?.DisplayName()));
        }

        _translation = translation;

        _translationConfigurationSource = translation == null
            ? null
            : configurationSource.Max(_translationConfigurationSource);

        return translation;
    }

    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public virtual ConfigurationSource? GetTranslationConfigurationSource()

View on GitHub (pinned to dbf9771522)

Solutions

  1. Remove the HasTranslation call from TVF and aggregate functions; custom translation applies only to scalar functions.
  2. Guard: only set translation when the function is scalar and non-aggregate.
  3. For TVFs, map them as queryable functions returning an entity type instead of providing a translation.
  4. If the function is meant to be scalar, ensure its return type is a scalar so IsScalar is true and IsAggregate stays false.

Example fix

// before
public IQueryable<Order> RecentOrders() => /* tvf */;
modelBuilder.HasDbFunction(typeof(Ctx).GetMethod("RecentOrders"))
    .HasTranslation(args => /* SqlExpression */ null!); // throws

// after
public IQueryable<Order> RecentOrders() => /* tvf */;
modelBuilder.HasDbFunction(typeof(Ctx).GetMethod("RecentOrders"));
// HasTranslation only for scalar fns:
public decimal TaxFor(decimal amount) => 0m;
modelBuilder.HasDbFunction(typeof(Ctx).GetMethod("TaxFor"))
    .HasTranslation(args => new SqlBinaryExpression(...));
Defensive patterns

Strategy: validation

Validate before calling

// Only set translation for scalar, non-aggregate functions
if (fn.IsScalar && !fn.IsAggregate)
{
    fn.Builder.HasTranslation(translate, fromDataAnnotation: true);
}

Type guard

static bool SupportsCustomTranslation(IDbFunction fn) => fn is { IsScalar: true, IsAggregate: false };

Prevention

When it happens

Trigger: Calling HasTranslation(...) on a table-valued function (IQueryable<T> return) or on a function marked IsAggregate. Surfaced through InternalDbFunctionBuilder.HasTranslation -> Metadata.SetTranslation.

Common situations: Applying a shared DbFunction configuration routine that calls HasTranslation to all functions, including TVFs and aggregates. Or trying to translate a TVF's row set the way you would a scalar.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/512631e12a7cb1e4. Report an issue: GitHub.