C# 顺序Guid生成器

C# 顺序Guid生成器,c#,guid,sequence,C#,Guid,Sequence,是否有任何方法可以获得Sql Server 2005+顺序Guid生成器的功能,而无需插入记录以在往返过程中读取它或调用本机win dll调用?我看到有人用一种使用rpcrt4.dll的方法回答了这个问题,但我不确定这是否能够在我的托管生产环境中工作 编辑:使用@John Boker的答案,我试图将其转换为更多的GuidComb生成器,而不是依赖于最后生成的Guid,而不是重新开始。而不是以Guid.Empty开头 public SequentialGuid() { var tempGu

是否有任何方法可以获得Sql Server 2005+顺序Guid生成器的功能,而无需插入记录以在往返过程中读取它或调用本机win dll调用?我看到有人用一种使用rpcrt4.dll的方法回答了这个问题,但我不确定这是否能够在我的托管生产环境中工作

编辑:使用@John Boker的答案,我试图将其转换为更多的GuidComb生成器,而不是依赖于最后生成的Guid,而不是重新开始。而不是以Guid.Empty开头

public SequentialGuid()
{
    var tempGuid = Guid.NewGuid();
    var bytes = tempGuid.ToByteArray();
    var time = DateTime.Now;
    bytes[3] = (byte) time.Year;
    bytes[2] = (byte) time.Month;
    bytes[1] = (byte) time.Day;
    bytes[0] = (byte) time.Hour;
    bytes[5] = (byte) time.Minute;
    bytes[4] = (byte) time.Second;
    CurrentGuid = new Guid(bytes);
}
我是根据我的评论得出这个结论的

// 3 - the least significant byte in Guid ByteArray 
        [for SQL Server ORDER BY clause]
// 10 - the most significant byte in Guid ByteArray 
        [for SQL Server ORDERY BY clause]
SqlOrderMap = new[] {3, 2, 1, 0, 5, 4, 7, 6, 9, 8, 15, 14, 13, 12, 11, 10};

这看起来像是我想要用DateTime为guid添加种子的方式,还是像是我应该从SqlOrderMap索引的末尾开始反向操作?我不太担心它们在创建初始guid的任何时候都会成为分页中断,因为它只会在应用程序回收期间出现。

这个人想出了一些方法来创建顺序guid,下面是一个链接

相关代码:

public class SequentialGuid {
    Guid _CurrentGuid;
    public Guid CurrentGuid {
        get {
            return _CurrentGuid;
        }
    }

    public SequentialGuid() {
        _CurrentGuid = Guid.NewGuid();
    }

    public SequentialGuid(Guid previousGuid) {
        _CurrentGuid = previousGuid;
    }

    public static SequentialGuid operator++(SequentialGuid sequentialGuid) {
        byte[] bytes = sequentialGuid._CurrentGuid.ToByteArray();
        for (int mapIndex = 0; mapIndex < 16; mapIndex++) {
            int bytesIndex = SqlOrderMap[mapIndex];
            bytes[bytesIndex]++;
            if (bytes[bytesIndex] != 0) {
                break; // No need to increment more significant bytes
            }
        }
        sequentialGuid._CurrentGuid = new Guid(bytes);
        return sequentialGuid;
    }

    private static int[] _SqlOrderMap = null;
    private static int[] SqlOrderMap {
        get {
            if (_SqlOrderMap == null) {
                _SqlOrderMap = new int[16] {
                    3, 2, 1, 0, 5, 4, 7, 6, 9, 8, 15, 14, 13, 12, 11, 10
                };
                // 3 - the least significant byte in Guid ByteArray [for SQL Server ORDER BY clause]
                // 10 - the most significant byte in Guid ByteArray [for SQL Server ORDERY BY clause]
            }
            return _SqlOrderMap;
        }
    }
}
公共类顺序GUID{
Guid _CurrentGuid;
公共Guid当前Guid{
得到{
返回_CurrentGuid;
}
}
公共顺序GUID(){
_CurrentGuid=Guid.NewGuid();
}
公共顺序Guid(Guid previousGuid){
_CurrentGuid=previousGuid;
}
公共静态SequentialGuid运算符++(SequentialGuid SequentialGuid){
byte[]bytes=sequentialGuid.\u CurrentGuid.ToByteArray();
对于(int-mapIndex=0;mapIndex<16;mapIndex++){
int bytesIndex=SqlOrderMap[mapIndex];
字节[字节索引]+;
if(字节[字节索引]!=0){
break;//无需增加更多有效字节
}
}
顺序Guid.\u CurrentGuid=新Guid(字节);
返回顺序GUID;
}
私有静态int[]_SqlOrderMap=null;
私有静态int[]SqlOrderMap{
得到{
if(_SqlOrderMap==null){
_SqlOrderMap=newint[16]{
3, 2, 1, 0, 5, 4, 7, 6, 9, 8, 15, 14, 13, 12, 11, 10
};
//3-Guid ByteArray中的最低有效字节[对于SQL Server ORDER BY子句]
//10-Guid ByteArray中的最高有效字节[对于SQL Server ORDERY BY子句]
}
返回_SqlOrderMap;
}
}
}

据我所知,NHibernate有一个特殊的生成器,叫做GuidCombGenerator。您可以查看它。

您可以使用相同的:

并应用一些位移位,将值按大端顺序排列

既然你想用C#:

另见


微软的
UuidCreateSequential
只是一个类型1uuid的实现

uuid有三个重要部分:

  • 节点
    :(6字节)-计算机的MAC地址
  • 时间戳
    :(7字节)-自1582年10月15日00:00:00.00(公历改为基督教日历的日期)起的100 ns间隔数
  • clockSequenceNumber
    (2字节)-计数器,以防生成速度超过100ns的guid,或更改mac地址
基本算法是:

  • 获取系统范围的锁
  • 从持久存储(注册表/文件)读取最后一个
    节点
    时间戳
    时钟序列号
  • 获取当前
    节点
    (即MAC地址)
  • 获取当前的
    时间戳
    • a) 如果保存的状态不可用或已损坏,或者mac地址已更改,则生成随机的
      clockSequenceNumber
    • b) 如果状态可用,但当前的
      时间戳
      与保存的时间戳相同或更早,则增加
      时钟序列号
  • 节点
    时间戳
    时钟序列号
    保存回持久存储
  • 释放全局锁
  • 根据rfc设置guid结构的格式
  • 有一个4位版本号和2位变体也需要输入到数据中:

    guid = new Guid(
          timestamp & 0xFFFFFFFF,  //timestamp low
          (timestamp >> 32) & 0xFFFF, //timestamp mid
          ((timestamp >> 40) & 0x0FFF), | (1 << 12) //timestamp high and version (version 1)
          (clockSequenceNumber & 0x3F) | (0x80), //clock sequence number and reserved
          node[0], node[1], node[2], node[3], node[4], node[5], node[6]);
    
    guid=新的guid(
    时间戳&0xFFFFFFFF,//时间戳低
    (时间戳>>32)&0xFFFF,//时间戳mid
    ((timestamp>>40)&0x0FFF),|(1我的解决方案(在VB中,但易于转换)。它将GUID的最重要(用于SQL Server排序)前8个字节更改为DateTime.UtcNow.Ticks,并且还有额外的代码,可以帮助您在调用新GUID比系统时钟更新快的情况下多次获取相同的Ticks

    Private ReadOnly _toSeqGuidLock As New Object()
    ''' <summary>
    ''' Replaces the most significant eight bytes of the GUID (according to SQL Server ordering) with the current UTC-timestamp.
    ''' </summary>
    ''' <remarks>Thread-Safe</remarks>
    <System.Runtime.CompilerServices.Extension()> _
    Public Function ToSeqGuid(ByVal guid As Guid) As Guid
    
        Static lastTicks As Int64 = -1
    
        Dim ticks = DateTime.UtcNow.Ticks
    
        SyncLock _toSeqGuidLock
    
            If ticks <= lastTicks Then
                ticks = lastTicks + 1
            End If
    
            lastTicks = ticks
    
        End SyncLock
    
        Dim ticksBytes = BitConverter.GetBytes(ticks)
    
        Array.Reverse(ticksBytes)
    
        Dim guidBytes = guid.ToByteArray()
    
        Array.Copy(ticksBytes, 0, guidBytes, 10, 6)
        Array.Copy(ticksBytes, 6, guidBytes, 8, 2)
    
        Return New Guid(guidBytes)
    
    End Function
    
    Private ReadOnly\u将guidlock设置为新对象()
    ''' 
    ''将GUID最重要的八个字节(根据SQL Server顺序)替换为当前UTC时间戳。
    ''' 
    “线程安全”
    _
    公共函数将seqguid(ByVal guid作为guid)作为guid
    静态lastTicks为Int64=-1
    Dim ticks=DateTime.UtcNow.ticks
    同步锁
    如果勾号C#版本

    公共静态Guid到eqguid()
    {
    Int64 lastTicks=-1;
    长刻度=System.DateTime.UtcNow.ticks;
    
    如果(勾号可以找到经常更新(至少每毫秒3次)的顺序guid。它是用常规C代码创建的(无本机代码调用)。

    是NHibernate实现guid的方式。Comb算法:

    private Guid GenerateComb()
    {
        byte[] guidArray = Guid.NewGuid().ToByteArray();
    
        DateTime baseDate = new DateTime(1900, 1, 1);
        DateTime now = DateTime.UtcNow;
    
        // Get the days and milliseconds which will be used to build the byte string 
        TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);
        TimeSpan msecs = now.TimeOfDay;
    
        // Convert to a byte array 
        // Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
        byte[] daysArray = BitConverter.GetBytes(days.Days);
        byte[] msecsArray = BitConverter.GetBytes((long) (msecs.TotalMilliseconds / 3.333333));
    
        // Reverse the bytes to match SQL Servers ordering 
        Array.Reverse(daysArray);
        Array.Reverse(msecsArray);
    
        // Copy the bytes into the guid 
        Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2);
        Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4);
    
        return new Guid(guidArray);
    }
    

    与其他建议进行比较可能很有趣:

    EntityFramework核心还实现了一个sequentialGuidValueGenerator。 它们为每个值生成随机GUID,并且仅根据时间戳和线程安全增量更改最重要的字节,以便在SQL Server中进行排序


    这将导致所有值都非常不同,但具有可排序的时间戳。

    不是专门的guid,但我现在通常使用雪花样式的顺序id生成器
    Private ReadOnly _toSeqGuidLock As New Object()
    ''' <summary>
    ''' Replaces the most significant eight bytes of the GUID (according to SQL Server ordering) with the current UTC-timestamp.
    ''' </summary>
    ''' <remarks>Thread-Safe</remarks>
    <System.Runtime.CompilerServices.Extension()> _
    Public Function ToSeqGuid(ByVal guid As Guid) As Guid
    
        Static lastTicks As Int64 = -1
    
        Dim ticks = DateTime.UtcNow.Ticks
    
        SyncLock _toSeqGuidLock
    
            If ticks <= lastTicks Then
                ticks = lastTicks + 1
            End If
    
            lastTicks = ticks
    
        End SyncLock
    
        Dim ticksBytes = BitConverter.GetBytes(ticks)
    
        Array.Reverse(ticksBytes)
    
        Dim guidBytes = guid.ToByteArray()
    
        Array.Copy(ticksBytes, 0, guidBytes, 10, 6)
        Array.Copy(ticksBytes, 6, guidBytes, 8, 2)
    
        Return New Guid(guidBytes)
    
    End Function
    
        public static Guid ToSeqGuid()
        {
            Int64 lastTicks = -1;
            long ticks = System.DateTime.UtcNow.Ticks;
    
            if (ticks <= lastTicks)
            {
                ticks = lastTicks + 1;
            }
    
            lastTicks = ticks;
    
            byte[] ticksBytes = BitConverter.GetBytes(ticks);
    
            Array.Reverse(ticksBytes);
    
            Guid myGuid = new Guid();
            byte[] guidBytes = myGuid.ToByteArray();
    
            Array.Copy(ticksBytes, 0, guidBytes, 10, 6);
            Array.Copy(ticksBytes, 6, guidBytes, 8, 2);
    
            Guid newGuid = new Guid(guidBytes);
    
            string filepath = @"C:\temp\TheNewGuids.txt";
            using (StreamWriter writer = new StreamWriter(filepath, true))
            {
                writer.WriteLine("GUID Created =  " + newGuid.ToString());
            }
    
            return newGuid;
    
        }
    
    }
    
    private Guid GenerateComb()
    {
        byte[] guidArray = Guid.NewGuid().ToByteArray();
    
        DateTime baseDate = new DateTime(1900, 1, 1);
        DateTime now = DateTime.UtcNow;
    
        // Get the days and milliseconds which will be used to build the byte string 
        TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);
        TimeSpan msecs = now.TimeOfDay;
    
        // Convert to a byte array 
        // Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
        byte[] daysArray = BitConverter.GetBytes(days.Days);
        byte[] msecsArray = BitConverter.GetBytes((long) (msecs.TotalMilliseconds / 3.333333));
    
        // Reverse the bytes to match SQL Servers ordering 
        Array.Reverse(daysArray);
        Array.Reverse(msecsArray);
    
        // Copy the bytes into the guid 
        Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2);
        Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4);
    
        return new Guid(guidArray);
    }
    
    using System;
    
    namespace Atlas.Core.Kernel.Extensions
    {
      public static class Guids
      {
        public static Guid Comb(this Guid source)
        {
          byte[] guidArray = source.ToByteArray();
    
          DateTime baseDate = new DateTime(1900, 1, 1);
          DateTime now = DateTime.Now;
    
          // Get the days and milliseconds which will be used to build the byte string 
          TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);
          TimeSpan msecs = now.TimeOfDay;
    
          // Convert to a byte array 
          // Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
          byte[] daysArray = BitConverter.GetBytes(days.Days);
          byte[] msecsArray = BitConverter.GetBytes((long)(msecs.TotalMilliseconds / 3.333333));
    
          // Reverse the bytes to match SQL Servers ordering 
          Array.Reverse(daysArray);
          Array.Reverse(msecsArray);
    
          // Copy the bytes into the guid 
          Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2);
          Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4);
    
          return new Guid(guidArray);
        }
      }
    }