This repository has been archived by the owner on Dec 13, 2019. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(guid): add support for generating a sequential Guid
- Loading branch information
Showing
2 changed files
with
78 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
using System; | ||
using System.Linq; | ||
|
||
namespace Cobweb | ||
{ | ||
/// <summary> | ||
/// Methods to build a sequential <see cref="Guid"/> compatible with SQL Server sorting algorithms. | ||
/// </summary> | ||
public static class SequentialGuid { | ||
private const long BaselineTicks = 599266080000000000L; // new DateTime(1900, 1, 1).Ticks; | ||
|
||
/// <summary> | ||
/// Generate a new <see cref="Guid"/> that can be sorted against others generated through <see cref="SequentialGuid"/>. | ||
/// </summary> | ||
/// <remarks> | ||
/// <remarks>The seven most-significant bytes will be based on the current timestamp, leaving nine bytes for randomization. | ||
/// Using seven bytes allows for 227 years of timestamp-based sorting (1900-2127).</remarks> | ||
/// <remarks>This sequential guid is compatible with SQL Server GUID sorting algorithms.</remarks> | ||
/// </remarks> | ||
/// <returns>The sequential <see cref="Guid"/>.</returns> | ||
public static Guid NewGuid() { | ||
return Guid.NewGuid().ToSequentialGuid(); | ||
} | ||
|
||
/// <summary> | ||
/// Convert an existing <see cref="Guid"/> into a <see cref="SequentialGuid"/>. | ||
/// </summary> | ||
/// <param name="guid">An existing <see cref="Guid"/> to be converted to a <see cref="SequentialGuid"/>.</param> | ||
/// <remarks> | ||
/// <remarks>The seven most-significant bytes will be based on the current timestamp, leaving nine bytes for randomization. | ||
/// Using seven bytes allows for 227 years of timestamp-based sorting (1900-2127).</remarks> | ||
/// <remarks>This sequential guid is compatible with SQL Server GUID sorting algorithms.</remarks> | ||
/// </remarks> | ||
/// <returns>The sequential <see cref="Guid"/>.</returns> | ||
public static Guid ToSequentialGuid(this Guid guid) { | ||
byte[] guidArray = guid.ToByteArray(); | ||
|
||
var counter = DateTime.UtcNow.Ticks - BaselineTicks; | ||
byte[] counterArray = BitConverter.GetBytes(counter); | ||
|
||
// We only need seven bytes. Seven bytes of ticks is 227 years. | ||
// This leaves nine bytes of randomization. | ||
counterArray = counterArray.Take(7).ToArray(); | ||
|
||
// Reverse the array to match SQL Server sort algorithm | ||
Array.Reverse(counterArray); | ||
|
||
// Copy the bytes into the byte array. | ||
// Priority of position in SQL Sorting: | ||
// 10, 11, 12, 13, 14, 15, 8, 9. | ||
Array.Copy(counterArray, 0, guidArray, 10, 6); | ||
Array.Copy(counterArray, 6, guidArray, 8, 1); | ||
|
||
return new Guid(guidArray); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using FluentAssertions; | ||
using NUnit.Framework; | ||
|
||
namespace Cobweb.Tests { | ||
[TestFixture] | ||
public class GivenSequentialGuid { | ||
[Test] | ||
public void ItShouldGenerateUniqueGuids() { | ||
const int generatedValueCount = 1000; | ||
var values = new HashSet<Guid>(); | ||
for (var i = 0; i < generatedValueCount; i++) { | ||
values.Add(SequentialGuid.NewGuid()); | ||
} | ||
|
||
// All values should be unique | ||
values.Count.Should().Be(generatedValueCount); | ||
} | ||
} | ||
} |