Skip to content

Commit a79751d

Browse files
committed
Switch to using Guid.CreateVersion7 in NpgsqlSequentialGuidValueGenerator
Closes #3574
1 parent 64d05b1 commit a79751d

File tree

1 file changed

+6
-88
lines changed

1 file changed

+6
-88
lines changed

src/EFCore.PG/ValueGeneration/NpgsqlSequentialGuidValueGenerator.cs

Lines changed: 6 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -10,98 +10,16 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.ValueGeneration;
1010
public class NpgsqlSequentialGuidValueGenerator : ValueGenerator<Guid>
1111
{
1212
/// <summary>
13-
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
14-
/// directly from your code. This API may change or be removed in future releases.
13+
/// Gets a value to be assigned to a property.
1514
/// </summary>
16-
public override bool GeneratesTemporaryValues => false;
15+
/// <param name="entry">The change tracking entry of the entity for which the value is being generated.</param>
16+
/// <returns>The value to be assigned to a property.</returns>
17+
public override Guid Next(EntityEntry entry)
18+
=> Guid.CreateVersion7();
1719

1820
/// <summary>
1921
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
2022
/// directly from your code. This API may change or be removed in future releases.
2123
/// </summary>
22-
public override Guid Next(EntityEntry entry) => BorrowedFromNet9.CreateVersion7(timestamp: DateTimeOffset.UtcNow);
23-
24-
// Code borrowed from .NET 9 should be removed as soon as the target framework includes such code
25-
#region Borrowed from .NET 9
26-
27-
#pragma warning disable IDE0007 // Use implicit type -- Avoid changes to code borrowed from BCL
28-
29-
// https://github.com/dotnet/runtime/blob/f402418aaed508c1d77e41b942e3978675183bfc/src/libraries/System.Private.CoreLib/src/System/Guid.cs
30-
internal static class BorrowedFromNet9
31-
{
32-
private const byte Variant10xxMask = 0xC0;
33-
private const byte Variant10xxValue = 0x80;
34-
35-
private const ushort VersionMask = 0xF000;
36-
private const ushort Version7Value = 0x7000;
37-
38-
/// <summary>Creates a new <see cref="Guid" /> according to RFC 9562, following the Version 7 format.</summary>
39-
/// <returns>A new <see cref="Guid" /> according to RFC 9562, following the Version 7 format.</returns>
40-
/// <remarks>
41-
/// <para>This uses <see cref="DateTimeOffset.UtcNow" /> to determine the Unix Epoch timestamp source.</para>
42-
/// <para>This seeds the rand_a and rand_b sub-fields with random data.</para>
43-
/// </remarks>
44-
public static Guid CreateVersion7() => CreateVersion7(DateTimeOffset.UtcNow);
45-
46-
/// <summary>Creates a new <see cref="Guid" /> according to RFC 9562, following the Version 7 format.</summary>
47-
/// <param name="timestamp">The date time offset used to determine the Unix Epoch timestamp.</param>
48-
/// <returns>A new <see cref="Guid" /> according to RFC 9562, following the Version 7 format.</returns>
49-
/// <exception cref="ArgumentOutOfRangeException"><paramref name="timestamp" /> represents an offset prior to <see cref="DateTimeOffset.UnixEpoch" />.</exception>
50-
/// <remarks>
51-
/// <para>This seeds the rand_a and rand_b sub-fields with random data.</para>
52-
/// </remarks>
53-
public static Guid CreateVersion7(DateTimeOffset timestamp)
54-
{
55-
// NewGuid uses CoCreateGuid on Windows and Interop.GetCryptographicallySecureRandomBytes on Unix to get
56-
// cryptographically-secure random bytes. We could use Interop.BCrypt.BCryptGenRandom to generate the random
57-
// bytes on Windows, as is done in RandomNumberGenerator, but that's measurably slower than using CoCreateGuid.
58-
// And while CoCreateGuid only generates 122 bits of randomness, the other 6 bits being for the version / variant
59-
// fields, this method also needs those bits to be non-random, so we can just use NewGuid for efficiency.
60-
var result = Guid.NewGuid();
61-
62-
// 2^48 is roughly 8925.5 years, which from the Unix Epoch means we won't
63-
// overflow until around July of 10,895. So there isn't any need to handle
64-
// it given that DateTimeOffset.MaxValue is December 31, 9999. However, we
65-
// can't represent timestamps prior to the Unix Epoch since UUIDv7 explicitly
66-
// stores a 48-bit unsigned value, so we do need to throw if one is passed in.
67-
68-
var unix_ts_ms = timestamp.ToUnixTimeMilliseconds();
69-
ArgumentOutOfRangeException.ThrowIfNegative(unix_ts_ms, nameof(timestamp));
70-
71-
ref var resultClone = ref Unsafe.As<Guid, GuidDoppleganger>(ref result); // Deviation from BLC: Reinterpret Guid as our own type so that we can manipulate its private fields
72-
73-
Unsafe.AsRef(in resultClone._a) = (int)(unix_ts_ms >> 16);
74-
Unsafe.AsRef(in resultClone._b) = (short)unix_ts_ms;
75-
76-
Unsafe.AsRef(in resultClone._c) = (short)(resultClone._c & ~VersionMask | Version7Value);
77-
Unsafe.AsRef(in resultClone._d) = (byte)(resultClone._d & ~Variant10xxMask | Variant10xxValue);
78-
79-
return result;
80-
}
81-
}
82-
83-
/// <summary>
84-
/// Used to manipulate the private fields of a <see cref="Guid"/> like its internal methods do, by treating a <see cref="Guid"/> as a <see cref="GuidDoppleganger"/>.
85-
/// </summary>
86-
[StructLayout(LayoutKind.Sequential)]
87-
internal readonly struct GuidDoppleganger
88-
{
89-
#pragma warning disable IDE1006 // Naming Styles -- Avoid further changes to code borrowed from BCL when working with the current type
90-
internal readonly int _a; // Do not rename (binary serialization)
91-
internal readonly short _b; // Do not rename (binary serialization)
92-
internal readonly short _c; // Do not rename (binary serialization)
93-
internal readonly byte _d; // Do not rename (binary serialization)
94-
internal readonly byte _e; // Do not rename (binary serialization)
95-
internal readonly byte _f; // Do not rename (binary serialization)
96-
internal readonly byte _g; // Do not rename (binary serialization)
97-
internal readonly byte _h; // Do not rename (binary serialization)
98-
internal readonly byte _i; // Do not rename (binary serialization)
99-
internal readonly byte _j; // Do not rename (binary serialization)
100-
internal readonly byte _k; // Do not rename (binary serialization)
101-
#pragma warning restore IDE1006 // Naming Styles
102-
}
103-
104-
#pragma warning restore IDE0007 // Use implicit type
105-
106-
#endregion
24+
public override bool GeneratesTemporaryValues => false;
10725
}

0 commit comments

Comments
 (0)