diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
index c412f98f98d..b7ef50f00fa 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
+++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
@@ -279,6 +279,14 @@ public static string DbFunctionNonScalarCustomTranslation(object? function)
GetString("DbFunctionNonScalarCustomTranslation", nameof(function)),
function);
+ ///
+ /// The DbFunction '{function}' returns a SqlExpression of type '{type}', which is a nullable value type. DbFunctions must return expressions with non-nullable value types, even when they may return null.
+ ///
+ public static string DbFunctionNullableValueReturnType(object? function, object? type)
+ => string.Format(
+ GetString("DbFunctionNullableValueReturnType", nameof(function), nameof(type)),
+ function, type);
+
///
/// The default value SQL has not been specified for the column '{table}.{column}'. Specify the SQL before using Entity Framework to create the database schema.
///
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx
index 2b06d916915..d10d59b928c 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.resx
+++ b/src/EFCore.Relational/Properties/RelationalStrings.resx
@@ -217,6 +217,9 @@
Cannot set custom translation on the DbFunction '{function}' since it is not a scalar function.
+
+ The DbFunction '{function}' returns a SqlExpression of type '{type}', which is a nullable value type. DbFunctions must return expressions with non-nullable value types, even when they may return null.
+
The default value SQL has not been specified for the column '{table}.{column}'. Specify the SQL before using Entity Framework to create the database schema.
diff --git a/src/EFCore.Relational/Query/RelationalMethodCallTranslatorProvider.cs b/src/EFCore.Relational/Query/RelationalMethodCallTranslatorProvider.cs
index 566f3d12bf8..1de2f16a1f3 100644
--- a/src/EFCore.Relational/Query/RelationalMethodCallTranslatorProvider.cs
+++ b/src/EFCore.Relational/Query/RelationalMethodCallTranslatorProvider.cs
@@ -60,8 +60,17 @@ public RelationalMethodCallTranslatorProvider(RelationalMethodCallTranslatorProv
{
if (dbFunction.Translation != null)
{
- return dbFunction.Translation.Invoke(
+ var translation = dbFunction.Translation.Invoke(
arguments.Select(e => _sqlExpressionFactory.ApplyDefaultTypeMapping(e)).ToList());
+
+ if (translation.Type.IsNullableValueType())
+ {
+ throw new InvalidOperationException(
+ RelationalStrings.DbFunctionNullableValueReturnType(
+ dbFunction.ModelName, dbFunction.ReturnType.ShortDisplayName()));
+ }
+
+ return translation;
}
var argumentsPropagateNullability = dbFunction.Parameters.Select(p => p.PropagatesNullability);
diff --git a/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs
index 9f524110d90..ce2cc778e72 100644
--- a/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs
@@ -224,6 +224,9 @@ public static string IdentityStringNonNullable(string s)
public static string IdentityStringNonNullableFluent(string s)
=> throw new Exception();
+ public static int? NullableValueReturnType()
+ => throw new NotImplementedException();
+
public string StringLength(string s)
=> throw new Exception();
@@ -310,6 +313,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
negated: false,
typeMapping: null));
+ modelBuilder.HasDbFunction(typeof(UDFSqlContext).GetMethod(nameof(NullableValueReturnType), Array.Empty()))
+ .HasTranslation(
+ _ => new SqlFunctionExpression(
+ "foo",
+ nullable: true,
+ typeof(int?),
+ typeMapping: null));
+
//Instance
modelBuilder.HasDbFunction(typeof(UDFSqlContext).GetMethod(nameof(CustomerOrderCountInstance)))
.HasName("CustomerOrderCount");
@@ -1009,6 +1020,23 @@ public virtual void Scalar_Function_with_nested_InExpression_translation()
Assert.Equal(4, query.Count);
}
+#if RELEASE
+ [ConditionalFact]
+ public virtual void Scalar_Function_with_nullable_value_return_type_throws()
+ {
+ using var context = CreateContext();
+
+ var exception = Assert.Throws(
+ () => context.Customers.Where(c => c.Id == UDFSqlContext.NullableValueReturnType()).ToList());
+
+ Assert.Equal(
+ RelationalStrings.DbFunctionNullableValueReturnType(
+ "Microsoft.EntityFrameworkCore.Query.UdfDbFunctionTestBase+UDFSqlContext.NullableValueReturnType()",
+ "int?"),
+ exception.Message);
+ }
+#endif
+
#endregion
#region Instance