diff --git a/src/EFCore.Relational/Extensions/RelationalDbFunctionsExtensions.cs b/src/EFCore.Relational/Extensions/RelationalDbFunctionsExtensions.cs
index f0f6a0e1ea8..bd427e791f3 100644
--- a/src/EFCore.Relational/Extensions/RelationalDbFunctionsExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalDbFunctionsExtensions.cs
@@ -56,4 +56,17 @@ public static class RelationalDbFunctionsExtensions
this DbFunctions _,
[NotParameterized] params T[] values)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Greatest)));
+
+ ///
+ /// Returns if equals , otherwise returns . Usually corresponds to the NULLIF SQL function.
+ ///
+ /// The type
+ /// The instance.
+ /// The expression.
+ /// The matching expression.
+ public static T? NullIf(
+ this DbFunctions _,
+ T expression,
+ T matchExpression)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Collate)));
}
diff --git a/src/EFCore.Relational/Query/Internal/NullIfTranslator.cs b/src/EFCore.Relational/Query/Internal/NullIfTranslator.cs
new file mode 100644
index 00000000000..794d9ec2330
--- /dev/null
+++ b/src/EFCore.Relational/Query/Internal/NullIfTranslator.cs
@@ -0,0 +1,52 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+
+namespace Microsoft.EntityFrameworkCore.Query.Internal;
+
+///
+/// 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.
+///
+public class NullIfTranslator : IMethodCallTranslator
+{
+ private static readonly MethodInfo MethodInfo
+ = typeof(RelationalDbFunctionsExtensions).GetMethod(nameof(RelationalDbFunctionsExtensions.NullIf))!;
+
+ private readonly ISqlExpressionFactory _sqlExpressionFactory;
+
+ ///
+ /// 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.
+ ///
+ public NullIfTranslator(ISqlExpressionFactory sqlExpressionFactory)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ }
+
+ ///
+ /// 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.
+ ///
+ public SqlExpression? Translate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ => method.IsGenericMethod
+ && Equals(method.GetGenericMethodDefinition(), MethodInfo)
+ ? _sqlExpressionFactory.Function(
+ "NULLIF",
+ new[] { arguments[1], arguments[2] },
+ nullable: true,
+ argumentsPropagateNullability: new[] { true, true },
+ method.ReturnType)
+ : null;
+}
diff --git a/src/EFCore.Relational/Query/RelationalMethodCallTranslatorProvider.cs b/src/EFCore.Relational/Query/RelationalMethodCallTranslatorProvider.cs
index 1de2f16a1f3..3d79bfeaed2 100644
--- a/src/EFCore.Relational/Query/RelationalMethodCallTranslatorProvider.cs
+++ b/src/EFCore.Relational/Query/RelationalMethodCallTranslatorProvider.cs
@@ -37,7 +37,8 @@ public RelationalMethodCallTranslatorProvider(RelationalMethodCallTranslatorProv
new GetValueOrDefaultTranslator(sqlExpressionFactory),
new ComparisonTranslator(sqlExpressionFactory),
new ByteArraySequenceEqualTranslator(sqlExpressionFactory),
- new RandomTranslator(sqlExpressionFactory)
+ new RandomTranslator(sqlExpressionFactory),
+ new NullIfTranslator(sqlExpressionFactory)
});
_sqlExpressionFactory = sqlExpressionFactory;
}
diff --git a/test/EFCore.Relational.Specification.Tests/Query/RelationalNorthwindDbFunctionsQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/RelationalNorthwindDbFunctionsQueryTestBase.cs
index db705b79cda..d2fd53d5e51 100644
--- a/test/EFCore.Relational.Specification.Tests/Query/RelationalNorthwindDbFunctionsQueryTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Query/RelationalNorthwindDbFunctionsQueryTestBase.cs
@@ -92,6 +92,16 @@ public virtual async Task Greatest_with_parameter_array_is_not_supported(bool as
ss => ss.Set().Where(od => EF.Functions.Greatest(arr) == 10251)));
}
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task NullIf(bool async)
+ => AssertCount(
+ async,
+ ss => ss.Set(),
+ ss => ss.Set(),
+ c => EF.Functions.NullIf(c.ContactName, "maria anders") == null,
+ c => c.ContactName == null);
+
protected abstract string CaseInsensitiveCollation { get; }
protected abstract string CaseSensitiveCollation { get; }
}