Come creare i guai deterministici

Nella nostra applicazione stiamo creando file Xml con un attributo che ha un valore Guid. Questo valore doveva essere coerente tra gli aggiornamenti dei file. Quindi, anche se tutto il resto nel file cambia, il valore guida per l’attributo dovrebbe rimanere lo stesso.

Una soluzione ovvia consisteva nel creare un dizionario statico con il nome file e i Guids da utilizzare per loro. Quindi ogni volta che generiamo il file, cerchiamo il dizionario per il nome file e usiamo il guid corrispondente. Ma questo non è fattibile perché potremmo scalare a 100 di file e non volevamo mantenere una grande lista di guids.

Quindi un altro approccio era quello di rendere lo stesso Guid basato sul percorso del file. Poiché i nostri percorsi di file e la struttura della directory dell’applicazione sono unici, il Guid dovrebbe essere unico per quel percorso. Quindi ogni volta che eseguiamo un aggiornamento, il file ottiene lo stesso guid in base al suo percorso. Ho trovato un modo interessante per generare tali ” Deterministic Guids ” (Grazie Elton Stoneman). Fondamentalmente fa questo:

private Guid GetDeterministicGuid(string input) { //use MD5 hash to get a 16-byte hash of the string: MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); byte[] inputBytes = Encoding.Default.GetBytes(input); byte[] hashBytes = provider.ComputeHash(inputBytes); //generate a guid from the hash: Guid hashGuid = new Guid(hashBytes); return hashGuid; } 

Quindi, data una stringa, la guida sarà sempre la stessa.

Ci sono altri approcci o modi consigliati per farlo? Quali sono i pro o i contro di questo metodo?

Come menzionato da @bacar, RFC 4122 §4.3 definisce un modo per creare un UUID basato sul nome. Il vantaggio di farlo (oltre all’uso di un hash MD5) è che questi sono garantiti per non entrare in collisione con UUID non basati su nomi e hanno una (molto) piccola possibilità di collisione con altri UUID basati sul nome.

Non c’è alcun supporto nativo in .NET Framework per la creazione di questi, ma ho pubblicato il codice su GitHub che implementa l’algoritmo. Può essere usato come segue:

 Guid guid = GuidUtility.Create(GuidUtility.UrlNamespace, filePath); 

Per ridurre ulteriormente il rischio di collisioni con altri GUID, è ansible creare un GUID privato da utilizzare come ID spazio dei nomi (anziché utilizzare l’ID spazio dei nomi URL definito nella RFC).

Questo convertirà qualsiasi stringa in una guida senza dover importare un assembly esterno.

 public static Guid ToGuid(string src) { byte[] stringbytes = Encoding.UTF8.GetBytes(src); byte[] hashedBytes = new System.Security.Cryptography .SHA1CryptoServiceProvider() .ComputeHash(stringbytes); Array.Resize(ref hashedBytes, 16); return new Guid(hashedBytes); } 

Esistono modi molto migliori per generare una guida unica, ma questo è un modo per aggiornare in modo coerente una chiave dati stringa in una chiave dati Guida.

Come dice Rob, il tuo metodo non genera un UUID, genera un hash simile a un UUID.

La RFC 4122 sugli UUID consente specificamente UUID deterministici (basati sui nomi) – le versioni 3 e 5 usano rispettivamente md5 e SHA1 (rispettivamente). La maggior parte delle persone ha probabilmente familiarità con la versione 4, che è casuale. Wikipedia offre una buona panoramica delle versioni. (Si noti che l’uso della parola “versione” qui sembra descrivere un “tipo” di UUID – la versione 5 non sostituisce la versione 4).

Sembra che ci siano poche librerie disponibili per generare UUID versione 3/5, incluso il modulo uuid python , boost.uuid (C ++) e OSSP UUID . (Non ho cercato nessuno .net)

MD5 è debole, credo che tu possa fare la stessa cosa con SHA-1 e ottenere risultati migliori.

A proposito, solo un parere personale, vestire un hash MD5 come GUID non lo rende un buon GUID. I GUID per loro natura non sono deterministici. questo sembra un imbroglione. Perché non basta chiamare un picche a picche e dire semplicemente che è una stringa resa hash dell’input. puoi farlo usando questa linea, piuttosto che la nuova linea guida:

 string stringHash = BitConverter.ToString(hashBytes) 

È necessario fare una distinzione tra le istanze della guida di class e gli identificatori che sono globalmente unici. Un “deterministico guid” è in realtà un hash (come dimostra la tua chiamata a provider.ComputeHash ). Gli hash hanno una probabilità molto maggiore di collisioni (due stringhe diverse che si verificano per produrre lo stesso hash) di Guid create tramite Guid.NewGuid .

Quindi il problema con il tuo approccio è che dovrai essere ok con la possibilità che due percorsi diversi produrranno lo stesso GUID. Se hai bisogno di un identificatore unico per ogni stringa di percorso, la cosa più semplice da fare è usare semplicemente la stringa . Se hai bisogno che la stringa venga oscurata dai tuoi utenti, crittografala – puoi usare ROT13 o qualcosa di più potente …

Tentare di calzare qualcosa che non è un puro GUID nel tipo di dati GUID potrebbe portare a problemi di manutenzione in futuro …