diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlGuidTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlGuidTranslator.cs
new file mode 100644
index 0000000000..f4e41f89fc
--- /dev/null
+++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlGuidTranslator.cs
@@ -0,0 +1,57 @@
+using static Npgsql.EntityFrameworkCore.PostgreSQL.Utilities.Statics;
+
+namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal;
+
+///
+/// Provides translation services for PostgreSQL UUID functions.
+///
+///
+/// See: https://www.postgresql.org/docs/current/datatype-uuid.html
+///
+public class NpgsqlGuidTranslator(ISqlExpressionFactory sqlExpressionFactory, Version? postgresVersion) : IMethodCallTranslator
+{
+ private readonly string _uuidGenerationFunction = postgresVersion.AtLeast(13) ? "gen_random_uuid" : "uuid_generate_v4";
+
+ ///
+ /// 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 virtual SqlExpression? Translate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ if (method.DeclaringType == typeof(Guid))
+ {
+ return method.Name switch
+ {
+ nameof(Guid.NewGuid)
+ => sqlExpressionFactory.Function(
+ _uuidGenerationFunction,
+ [],
+ nullable: false,
+ argumentsPropagateNullability: FalseArrays[0],
+ method.ReturnType),
+
+ // Note: uuidv7() was introduce in PostgreSQL 18.
+ // In NpgsqlEvaluatableExpressionFilter we only prevent local evaluation when targeting PG18 or later;
+ // that means that for lower version, the call gets evaluated locally and the result sent as a parameter
+ // (and we never see the method call here).
+ nameof(Guid.CreateVersion7)
+ => sqlExpressionFactory.Function(
+ "uuidv7",
+ [],
+ nullable: false,
+ argumentsPropagateNullability: FalseArrays[0],
+ method.ReturnType),
+
+ _ => null
+ };
+ }
+
+ return null;
+ }
+}
diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMethodCallTranslatorProvider.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMethodCallTranslatorProvider.cs
index 45e794efd1..964dd498da 100644
--- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMethodCallTranslatorProvider.cs
+++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMethodCallTranslatorProvider.cs
@@ -55,7 +55,7 @@ public NpgsqlMethodCallTranslatorProvider(
LTreeTranslator,
new NpgsqlMathTranslator(typeMappingSource, sqlExpressionFactory, model),
new NpgsqlNetworkTranslator(typeMappingSource, sqlExpressionFactory, model),
- new NpgsqlNewGuidTranslator(sqlExpressionFactory, npgsqlOptions.PostgresVersion),
+ new NpgsqlGuidTranslator(sqlExpressionFactory, npgsqlOptions.PostgresVersion),
new NpgsqlObjectToStringTranslator(typeMappingSource, sqlExpressionFactory),
new NpgsqlRandomTranslator(sqlExpressionFactory),
new NpgsqlRangeTranslator(typeMappingSource, sqlExpressionFactory, model, supportsMultiranges),
diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlNewGuidTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlNewGuidTranslator.cs
deleted file mode 100644
index 8762343ffc..0000000000
--- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlNewGuidTranslator.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using static Npgsql.EntityFrameworkCore.PostgreSQL.Utilities.Statics;
-
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal;
-
-///
-/// Provides translation services for PostgreSQL UUID functions.
-///
-///
-/// See: https://www.postgresql.org/docs/current/datatype-uuid.html
-///
-public class NpgsqlNewGuidTranslator : IMethodCallTranslator
-{
- private static readonly MethodInfo MethodInfo = typeof(Guid).GetRuntimeMethod(nameof(Guid.NewGuid), [])!;
-
- private readonly ISqlExpressionFactory _sqlExpressionFactory;
- private readonly string _uuidGenerationFunction;
-
- ///
- /// 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 NpgsqlNewGuidTranslator(ISqlExpressionFactory sqlExpressionFactory, Version? postgresVersion)
- {
- _sqlExpressionFactory = sqlExpressionFactory;
- _uuidGenerationFunction = postgresVersion.AtLeast(13) ? "gen_random_uuid" : "uuid_generate_v4";
- }
-
- ///
- /// 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 virtual SqlExpression? Translate(
- SqlExpression? instance,
- MethodInfo method,
- IReadOnlyList arguments,
- IDiagnosticsLogger logger)
- => MethodInfo.Equals(method)
- ? _sqlExpressionFactory.Function(
- _uuidGenerationFunction,
- [],
- nullable: false,
- argumentsPropagateNullability: FalseArrays[0],
- method.ReturnType)
- : null;
-}
diff --git a/src/EFCore.PG/Query/Internal/NpgsqlEvaluatableExpressionFilter.cs b/src/EFCore.PG/Query/Internal/NpgsqlEvaluatableExpressionFilter.cs
index da5ebbbd18..ffb9528260 100644
--- a/src/EFCore.PG/Query/Internal/NpgsqlEvaluatableExpressionFilter.cs
+++ b/src/EFCore.PG/Query/Internal/NpgsqlEvaluatableExpressionFilter.cs
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
@@ -10,6 +11,8 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
///
public class NpgsqlEvaluatableExpressionFilter : RelationalEvaluatableExpressionFilter
{
+ private readonly Version _postgresVersion;
+
private static readonly MethodInfo TsQueryParse =
typeof(NpgsqlTsQuery).GetRuntimeMethod(nameof(NpgsqlTsQuery.Parse), [typeof(string)])!;
@@ -24,9 +27,11 @@ public class NpgsqlEvaluatableExpressionFilter : RelationalEvaluatableExpression
///
public NpgsqlEvaluatableExpressionFilter(
EvaluatableExpressionFilterDependencies dependencies,
- RelationalEvaluatableExpressionFilterDependencies relationalDependencies)
+ RelationalEvaluatableExpressionFilterDependencies relationalDependencies,
+ INpgsqlSingletonOptions npgsqlSingletonOptions)
: base(dependencies, relationalDependencies)
{
+ _postgresVersion = npgsqlSingletonOptions.PostgresVersion;
}
///
@@ -51,6 +56,8 @@ public override bool IsEvaluatableExpression(Expression expression, IModel model
|| declaringType == typeof(NpgsqlNetworkDbFunctionsExtensions)
|| declaringType == typeof(NpgsqlJsonDbFunctionsExtensions)
|| declaringType == typeof(NpgsqlRangeDbFunctionsExtensions)
+ // PG18 introduced uuidv7(), so we prevent local evaluation when targeting PG18 or later.
+ || declaringType == typeof(Guid) && method.Name == nameof(Guid.CreateVersion7) && _postgresVersion.AtLeast(18)
// Prevent evaluation of ValueTuple.Create, see NewExpression of ITuple below
|| declaringType == typeof(ValueTuple) && method.Name == nameof(ValueTuple.Create))
{
diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/GuidTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/GuidTranslationsNpgsqlTest.cs
index 664e1b369f..e6b221cb84 100644
--- a/test/EFCore.PG.FunctionalTests/Query/Translations/GuidTranslationsNpgsqlTest.cs
+++ b/test/EFCore.PG.FunctionalTests/Query/Translations/GuidTranslationsNpgsqlTest.cs
@@ -1,3 +1,5 @@
+using Microsoft.EntityFrameworkCore.TestModels.BasicTypesModel;
+
namespace Microsoft.EntityFrameworkCore.Query.Translations;
public class GuidTranslationsNpgsqlTest : GuidTranslationsTestBase
@@ -71,6 +73,34 @@ WHERE uuid_generate_v4() <> '00000000-0000-0000-0000-000000000000'
}
}
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task CreateVersion7(bool async)
+ {
+ await AssertQuery(
+ async,
+ ss => ss.Set()
+ .Where(od => Guid.CreateVersion7() != default));
+
+ if (TestEnvironment.PostgresVersion >= new Version(18, 0))
+ {
+ AssertSql(
+ """
+SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan"
+FROM "BasicTypesEntities" AS b
+WHERE uuidv7() <> '00000000-0000-0000-0000-000000000000'
+""");
+ }
+ else
+ {
+ AssertSql(
+ """
+SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan"
+FROM "BasicTypesEntities" AS b
+""");
+ }
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}