using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography; namespace EasyDevCore.Common { /// /// /// public enum SequentialGuidType { /// /// The sequential as string (sepecific for MySQL, PostgreSQL ...) /// SequentialAsString, /// /// The sequential as binary (sepecific for Oracle) /// SequentialAsBinary, /// /// The sequential at end (sepecific for Microsoft SQL Server) /// SequentialAtEnd } /// /// /// public static class SequentialGuidGenerator { /// /// The _RNG /// private static readonly System.Security.Cryptography.RandomNumberGenerator _rng = System.Security.Cryptography.RandomNumberGenerator.Create(); /// /// Create a sequential GUID. /// /// Type of the GUID. /// public static Guid NewSequentialGuid(SequentialGuidType guidType) { return NewSequentialGuid(guidType, 1)[0]; } /// /// Nexts the sequential unique identifier. /// /// The unique identifier. /// Type of the unique identifier. /// public static Guid NextSequentialGuid(Guid guid, SequentialGuidType guidType) { byte[] guidBytes = guid.ToByteArray(); UInt32 nextID = 0; switch (guidType) { case SequentialGuidType.SequentialAsString: case SequentialGuidType.SequentialAsBinary: nextID = BitConverter.ToUInt32(guidBytes, 12) + 1; Buffer.BlockCopy(BitConverter.GetBytes(nextID), 0, guidBytes, 12, 4); break; case SequentialGuidType.SequentialAtEnd: nextID = BitConverter.ToUInt32(guidBytes, 6) + 1; Buffer.BlockCopy(BitConverter.GetBytes(nextID), 0, guidBytes, 6, 4); break; default: throw new NotSupportedException(guidType.ToString()); } return new Guid(guidBytes); } /// /// Nexts the sequential unique identifier. /// /// The unique identifier. /// public static Guid NextSequentialGuid(Guid guid) { return NextSequentialGuid(guid, SequentialGuidType.SequentialAtEnd); } /// /// Create a sequential GUID (sepecific for Microsoft SQL Server) /// (SequentialGuidType = SequentialGuidType.SequentialAtEnd). /// /// public static Guid NewSequentialGuid() { return NewSequentialGuid(SequentialGuidType.SequentialAtEnd, 1)[0]; } /// /// Create a sery sequential GUIDs (sepecific for Microsoft SQL Server) /// (SequentialGuidType = SequentialGuidType.SequentialAtEnd). /// /// The count. /// public static Guid[] NewSequentialGuid(int count) { return NewSequentialGuid(SequentialGuidType.SequentialAtEnd, count); } /// /// Create a sery sequential GUIDs. /// /// Type of the GUID. /// The count. /// public static Guid[] NewSequentialGuid(SequentialGuidType guidType, int count) { byte[] randomBytes = new byte[10]; _rng.GetBytes(randomBytes); UInt32 sequentialID = BitConverter.ToUInt32(randomBytes, 6); long timestamp = DateTime.UtcNow.Ticks / 10000L; byte[] timestampBytes = null; int increment = -1; Guid[] listGuids = new Guid[count]; byte[] guidBytes = new byte[16]; timestampBytes = BitConverter.GetBytes(timestamp); switch (guidType) { case SequentialGuidType.SequentialAsString: case SequentialGuidType.SequentialAsBinary: Buffer.BlockCopy(randomBytes, 0, guidBytes, 6, 6); //Get first 6 byte if (BitConverter.IsLittleEndian) { Array.Reverse(timestampBytes); } Buffer.BlockCopy(timestampBytes, 2, guidBytes, 0, 6); // If formatting as a string, we have to reverse the order // of the Data1 and Data2 blocks on little-endian systems. if (guidType == SequentialGuidType.SequentialAsString && BitConverter.IsLittleEndian) { Array.Reverse(guidBytes, 0, 4); Array.Reverse(guidBytes, 4, 2); } while (increment++ < count) { Buffer.BlockCopy(BitConverter.GetBytes(sequentialID++), 0, guidBytes, 12, 4); //Move last rest 4 byte listGuids[increment] = new Guid(guidBytes); } break; case SequentialGuidType.SequentialAtEnd: Buffer.BlockCopy(randomBytes, 0, guidBytes, 0, 6); //Get first 6 byte if (BitConverter.IsLittleEndian) { Array.Reverse(timestampBytes); } Buffer.BlockCopy(timestampBytes, 2, guidBytes, 10, 6); while (++increment < count) { Buffer.BlockCopy(BitConverter.GetBytes(sequentialID++), 0, guidBytes, 6, 4); //Move last rest 4 byte listGuids[increment] = new Guid(guidBytes); } break; } return listGuids; } } }