Skip to content

Commit

Permalink
Detect DbFunction returning a nullable value expression (#29588)
Browse files Browse the repository at this point in the history
Closes #29585
  • Loading branch information
roji committed Nov 16, 2022
1 parent 1596784 commit 3d869fb
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 1 deletion.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@
<data name="DbFunctionNonScalarCustomTranslation" xml:space="preserve">
<value>Cannot set custom translation on the DbFunction '{function}' since it is not a scalar function.</value>
</data>
<data name="DbFunctionNullableValueReturnType" xml:space="preserve">
<value>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.</value>
</data>
<data name="DefaultValueSqlUnspecified" xml:space="preserve">
<value>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.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -310,6 +313,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
negated: false,
typeMapping: null));

modelBuilder.HasDbFunction(typeof(UDFSqlContext).GetMethod(nameof(NullableValueReturnType), Array.Empty<Type>()))
.HasTranslation(
_ => new SqlFunctionExpression(
"foo",
nullable: true,
typeof(int?),
typeMapping: null));

//Instance
modelBuilder.HasDbFunction(typeof(UDFSqlContext).GetMethod(nameof(CustomerOrderCountInstance)))
.HasName("CustomerOrderCount");
Expand Down Expand Up @@ -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<InvalidOperationException>(
() => context.Customers.Where(c => c.Id == UDFSqlContext.NullableValueReturnType()).ToList());

Assert.Equal(
RelationalStrings.DbFunctionNullableValueReturnType(
"Microsoft.EntityFrameworkCore.Query.UdfDbFunctionTestBase<Microsoft.EntityFrameworkCore.Query.UdfDbFunctionSqlServerTests+SqlServer>+UDFSqlContext.NullableValueReturnType()",
"int?"),
exception.Message);
}
#endif

#endregion

#region Instance
Expand Down

0 comments on commit 3d869fb

Please sign in to comment.