From 126799d807d5b1454d0c2d72f5fb92f79fabc67f Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Sat, 15 Feb 2025 00:20:32 +0200 Subject: [PATCH] Generate UUIDv7 values for value-generated strings Fixes #3460 --- .../Internal/NpgsqlValueGeneratorSelector.cs | 15 ++++++++----- .../NpgsqlSequentialStringValueGenerator.cs | 22 +++++++++++++++++++ .../NpgsqlValueGeneratorSelectorTest.cs | 4 ++-- 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 src/EFCore.PG/ValueGeneration/NpgsqlSequentialStringValueGenerator.cs diff --git a/src/EFCore.PG/ValueGeneration/Internal/NpgsqlValueGeneratorSelector.cs b/src/EFCore.PG/ValueGeneration/Internal/NpgsqlValueGeneratorSelector.cs index a9274f617..808532b42 100644 --- a/src/EFCore.PG/ValueGeneration/Internal/NpgsqlValueGeneratorSelector.cs +++ b/src/EFCore.PG/ValueGeneration/Internal/NpgsqlValueGeneratorSelector.cs @@ -101,9 +101,14 @@ public override bool TrySelect(IProperty property, ITypeBase typeBase, out Value /// doing so can result in application failures when updating to a new Entity Framework Core release. /// protected override ValueGenerator? FindForType(IProperty property, ITypeBase typeBase, Type clrType) - => property.ClrType.UnwrapNullableType() == typeof(Guid) - ? property.ValueGenerated == ValueGenerated.Never || property.GetDefaultValueSql() is not null - ? new TemporaryGuidValueGenerator() - : new NpgsqlSequentialGuidValueGenerator() - : base.FindForType(property, typeBase, clrType); + => property.ClrType.UnwrapNullableType() switch + { + var t when t == typeof(Guid) && property.ValueGenerated is not ValueGenerated.Never && property.GetDefaultValueSql() is null + => new NpgsqlSequentialGuidValueGenerator(), + + var t when t == typeof(string) && property.ValueGenerated is not ValueGenerated.Never && property.GetDefaultValueSql() is null + => new NpgsqlSequentialStringValueGenerator(), + + _ => base.FindForType(property, typeBase, clrType) + }; } diff --git a/src/EFCore.PG/ValueGeneration/NpgsqlSequentialStringValueGenerator.cs b/src/EFCore.PG/ValueGeneration/NpgsqlSequentialStringValueGenerator.cs new file mode 100644 index 000000000..8c4f595dc --- /dev/null +++ b/src/EFCore.PG/ValueGeneration/NpgsqlSequentialStringValueGenerator.cs @@ -0,0 +1,22 @@ +namespace Npgsql.EntityFrameworkCore.PostgreSQL.ValueGeneration; + +/// +/// This API supports the Entity Framework Core infrastructure and is not intended to be used +/// directly from your code. This API may change or be removed in future releases. +/// +public class NpgsqlSequentialStringValueGenerator : ValueGenerator +{ + private readonly NpgsqlSequentialGuidValueGenerator _guidGenerator = new(); + + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public override bool GeneratesTemporaryValues => false; + + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public override string Next(EntityEntry entry) => _guidGenerator.Next(entry).ToString(); +} diff --git a/test/EFCore.PG.Tests/NpgsqlValueGeneratorSelectorTest.cs b/test/EFCore.PG.Tests/NpgsqlValueGeneratorSelectorTest.cs index 57b0d1b9a..9b6b3ae42 100644 --- a/test/EFCore.PG.Tests/NpgsqlValueGeneratorSelectorTest.cs +++ b/test/EFCore.PG.Tests/NpgsqlValueGeneratorSelectorTest.cs @@ -21,7 +21,7 @@ public void Returns_built_in_generators_for_types_setup_for_value_generation() AssertGenerator("NullableShort"); AssertGenerator("NullableByte"); AssertGenerator("Decimal"); - AssertGenerator("String"); + AssertGenerator("String"); AssertGenerator("Guid"); AssertGenerator("Binary"); } @@ -128,7 +128,7 @@ public void Returns_sequence_value_generators_when_configured_for_model() AssertGenerator>("NullableInt", setSequences: true); AssertGenerator>("NullableLong", setSequences: true); AssertGenerator>("NullableShort", setSequences: true); - AssertGenerator("String", setSequences: true); + AssertGenerator("String", setSequences: true); AssertGenerator("Guid", setSequences: true); AssertGenerator("Binary", setSequences: true); }