{"record":{"id":"512631e12a7cb1e4","repo":"dotnet/efcore","slug":"custom-translation-cannot-be-set-on-the-dbfunction","errorCode":null,"errorMessage":"Custom translation cannot be set on the DbFunction '{function}' since it is not a scalar function.","messagePattern":"Custom translation cannot be set on the DbFunction '(.+?)' since it is not a scalar function\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Metadata/Internal/DbFunction.cs","lineNumber":611,"sourceCode":"        set => SetTranslation(value, ConfigurationSource.Explicit);\n    }\n\n    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    public virtual Func<IReadOnlyList<SqlExpression>, SqlExpression>? SetTranslation(\n        Func<IReadOnlyList<SqlExpression>, SqlExpression>? translation,\n        ConfigurationSource configurationSource)\n    {\n        EnsureMutable();\n\n        if (translation != null\n            && (!IsScalar || IsAggregate))\n        {\n            throw new InvalidOperationException(RelationalStrings.DbFunctionNonScalarCustomTranslation(MethodInfo?.DisplayName()));\n        }\n\n        _translation = translation;\n\n        _translationConfigurationSource = translation == null\n            ? null\n            : configurationSource.Max(_translationConfigurationSource);\n\n        return translation;\n    }\n\n    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    public virtual ConfigurationSource? GetTranslationConfigurationSource()","sourceCodeStart":593,"sourceCodeEnd":629,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Metadata/Internal/DbFunction.cs#L593-L629","documentation":"Custom translation (a Func<IReadOnlyList<SqlExpression>,SqlExpression>) only makes sense for scalar functions where EF translates the call into a single SQL expression. SetTranslation throws when translation is non-null and the function is not scalar or is an aggregate, because there is no single SQL expression to produce for a row set.","triggerScenarios":"Calling HasTranslation / SetTranslation on a DbFunction that returns IQueryable<T> (a TVF) or is marked as an aggregate function.","commonSituations":"Copy-pasting scalar DbFunction configuration (which includes HasTranslation) onto a TVF; refactor that converted a scalar function into a TVF without dropping the custom translator.","solutions":["Remove the HasTranslation / SetTranslation call from the TVF DbFunction — TVFs are mapped by name/schema, not by expression translation.","If you genuinely need custom SQL expression translation, convert the function back to a scalar return type so it qualifies for a translator."],"exampleFix":"// before\npublic IQueryable<Order> Search(string term) => throw new NotSupportedException();\nmodelBuilder.HasDbFunction(typeof(AppDb).GetMethod(nameof(Search)))\n    .HasTranslation(args => new SqlFragmentExpression(\"...\"));\n\n// after\nmodelBuilder.HasDbFunction(typeof(AppDb).GetMethod(nameof(Search)));\n// TVF mapping is by schema/name; no HasTranslation","handlingStrategy":"validation","validationCode":"var fn = modelBuilder.HasDbFunction(typeof(AppDb).GetMethod(nameof(AppDb.Search))!).Metadata;\nif (fn.IsScalar && !fn.IsAggregate)\n{\n    // safe to call HasTranslation\n}\nelse\n{\n    // skip HasTranslation for TVF / aggregate\n}","typeGuard":"static bool SupportsCustomTranslation(IReadOnlyDbFunction fn) => fn.IsScalar && !fn.IsAggregate;","tryCatchPattern":null,"preventionTips":["Reserve HasTranslation for scalar DbFunctions only.","Document which functions are TVFs in your context so translators are not mistakenly attached."],"tags":["relational","dbfunctions","translation","table-valued-functions"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}