GUID sequenziale in Linq-to-Sql?

Ho appena letto un post sul blog sulla capacità di NHibernate di creare un GUID dall’ora di sistema (Guid.Comb), evitando così una buona quantità di frammentazione del database. È ansible chiamarlo equivalente sul lato client all’ID sequenziale di SQL Server.

C’è un modo per utilizzare una strategia simile nel mio progetto Linq-to-Sql (generando il Guid nel codice)?

I COMB vengono generati nel seguente modo:

DECLARE @aGuid UNIQUEIDENTIFIER SET @aGuid = CAST(CAST(NEWID() AS BINARY(10)) + CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER) 

Quale trascritto in C # sarebbe simile a questo:

  public static unsafe Guid CombGuid() { Guid guid = Guid.NewGuid(); byte[] bytes = guid.ToByteArray(); long ticks = DateTime.Now.Ticks; fixed( byte* pByte = bytes ) { int* pFirst = (int *)(pByte + 10); short* pNext = (short*)(pByte + 14); *pFirst = (int)(ticks & 0xFFFFFF00); *pNext = (short)ticks; } return new Guid( bytes ); } 

Codice C # (sicuro) (Complimenti del generatore di pettini Guid di NHibernate)

 Guid GenerateComb() { byte[] destinationArray = Guid.NewGuid().ToByteArray(); DateTime time = new DateTime(0x76c, 1, 1); DateTime now = DateTime.Now; TimeSpan span = new TimeSpan(now.Ticks - time.Ticks); TimeSpan timeOfDay = now.TimeOfDay; byte[] bytes = BitConverter.GetBytes(span.Days); byte[] array = BitConverter.GetBytes((long) (timeOfDay.TotalMilliseconds / 3.333333)); Array.Reverse(bytes); Array.Reverse(array); Array.Copy(bytes, bytes.Length - 2, destinationArray, destinationArray.Length - 6, 2); Array.Copy(array, array.Length - 4, destinationArray, destinationArray.Length - 4, 4); return new Guid(destinationArray); } 

Un link alla fonte su github: https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Id/GuidCombGenerator.cs

Bene, potresti generare il Guid a mano. Tuttavia, uno dei vantaggi di una guida è che non è ipotizzabile – vale a dire dato il record 0000-...-0005 , di solito c’è un piccolo punto (da un utente malintenzionato) che controlla il record 0000-....-0004 ecc. .

Inoltre – re frammentazione? Finché si dispone di un indice non cluster su questi dati, non sono sicuro che questo sia un problema. Normalmente non si inserisce un indice cluster su un Guid , quindi la tabella sarà un heap (a meno che non si disponga di un indice cluster separato, ad esempio un IDENTITY int). Nel qual caso aggiungerai alla fine e inserendo la nuova guida nell’indice non in cluster. Nessun vero dolore.

(modifica) Un problema nell’usare il tempo direttamente è che si introduce un rischio molto maggiore di collisioni; dovresti preoccuparti della creazione di Guid ciclo chiuso (evitare cioè la ripetizione quando ne crei alcuni in sequenza), il che significa sincronizzazione, ecc. e diventa ancora più problematico se più macchine lavorano in modo intensivo in parallelo – è probabile che otterrai duplicati.

Puoi sempre chiamare UuidCreateSequential; questo è il “vecchio” generatore di guida (pre-2000-ish quando MSFT lo ha modificato per i guai di stile più casuali a cui siamo abituati oggi). Hanno rinominato il vecchio UuidCreate in UuidCreateSequential e hanno inserito il loro nuovo generatore di guida in una nuova implementazione di UuidCreate. UuidCreateSequential è anche ciò che SQL Server utilizza in NewSequentialID (), ed è unico come i normali guids, ma con il vantaggio che sono sequenziali se ne si crea una pila in fila nello stesso processo.

 using System; using System.Runtime.InteropServices; namespace System { public static class GuidEx { [DllImport("rpcrt4.dll", SetLastError = true)] private static extern int UuidCreateSequential(out Guid guid); private const int RPC_S_OK = 0; ///  /// Generate a new sequential GUID. If UuidCreateSequential fails, it will fall back on standard random guids. ///  /// A GUID public static Guid NewSeqGuid() { Guid sequentialGuid; int hResult = UuidCreateSequential(out sequentialGuid); if (hResult == RPC_S_OK) { return sequentialGuid; } else { //couldn't create sequential guid, fall back on random guid return Guid.NewGuid(); } } } } 

@arul, @Doug

Perché hai inserito la parte temporale alla fine del GUID?

Ho pensato che i byte principali siano più significativi per l’ordine e l’ordine è il motivo per cui la parte temporale è stata introdotta in primo luogo per impedire la frammentazione dell’indice.

Ok, ho trovato la risposta , e questa risposta di Bernhard Kircher e del sito Confronto tra GUID e uniqueidentifier Values ​​(ADO.NET) a cui fa riferimento.

I GUID generati in questo modo non avrebbero quindi funzionato allo stesso modo su altri database rispetto a MS SQL-Server, ma questo non è correlato a LINQ-to-SQL.

Ci scusiamo per gli URL deformati ma non ho abbastanza reputazione per pubblicare più link.

Abbiamo utilizzato un metodo simile a quello che Doug ha pubblicato in precedenza nel modello di Entity Framework, quindi devi essere in grado di farlo anche utilizzando Linq in SQL.

Mentre facevamo questo, avevamo bisogno di un generatore di pettine per test, e abbiamo finito per build questo piccolo strumento per generare pettini online

http://www.webdesigncompany.co.uk/comb-guid/

Spero che ti possa aiutare anche tu.