web-dev-qa-db-fra.com

Comment créer des guides déterministes

Dans notre application, nous créons des fichiers Xml avec un attribut qui a une valeur Guid. Cette valeur devait être cohérente entre les mises à niveau de fichiers. Ainsi, même si tout le reste du fichier change, la valeur guid de l'attribut doit rester la même.

Une solution évidente était de créer un dictionnaire statique avec le nom de fichier et les GUID à utiliser pour eux. Ensuite, chaque fois que nous générons le fichier, nous recherchons le nom du fichier dans le dictionnaire et utilisons le guide correspondant. Mais ce n'est pas faisable car nous pourrions évoluer vers des centaines de fichiers et ne voulions pas maintenir une grande liste de guides.

Une autre approche consistait donc à rendre le Guid identique en fonction du chemin d'accès au fichier. Étant donné que nos chemins d'accès aux fichiers et la structure du répertoire d'application sont uniques, le Guid doit être unique pour ce chemin d'accès. Ainsi, chaque fois que nous exécutons une mise à niveau, le fichier reçoit le même guid en fonction de son chemin. J'ai trouvé une façon intéressante de générer de tels ' Deterministic Guids ' (Merci Elton Stoneman). Il fait essentiellement ceci:

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; 

} 

Donc, étant donné une chaîne, le Guid sera toujours le même.

Existe-t-il d'autres approches ou des moyens recommandés pour y parvenir? Quels sont les avantages ou les inconvénients de cette méthode?

94
Punit Vora

Comme mentionné par @bacar, RFC 4122 §4.3 définit un moyen de créer un UUID basé sur le nom. L'avantage de faire cela (par rapport à l'utilisation d'un hachage MD5) est que ceux-ci sont garantis de ne pas entrer en collision avec des UUID non nommés et ont une très (très) petite possibilité de collision avec d'autres UUID basés sur le nom.

Il n'y a pas de support natif dans le .NET Framework pour les créer, mais j'ai posté code sur GitHub qui implémente l'algorithme. Il peut être utilisé comme suit:

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

Pour réduire encore plus le risque de collision avec d'autres GUID, vous pouvez créer un GUID privé à utiliser comme ID d'espace de noms (au lieu d'utiliser l'ID d'espace de noms URL défini dans le RFC).

140
Bradley Grainger

Cela convertira n'importe quelle chaîne en Guid sans avoir à importer un assembly extérieur.

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);
}

Il existe de bien meilleures façons de générer un GUID unique, mais c'est un moyen de mettre à niveau de manière cohérente une clé de données de chaîne vers une clé de données de GUID.

28
Ben Gripka

Comme Rob le mentionne, votre méthode ne génère pas d'UUID, elle génère un hachage qui ressemble à un UUID.

RFC 4122 sur les UUID permet spécifiquement les UUID déterministes (basés sur le nom) - Les versions 3 et 5 utilisent respectivement md5 et SHA1. La plupart des gens connaissent probablement la version 4, qui est aléatoire. Wikipedia donne un bon aperçu des versions. (Notez que l'utilisation de la "version" de Word ici semble décrire un "type" d'UUID - la version 5 ne remplace pas la version 4).

Il semble y avoir quelques bibliothèques pour générer des UUID de version 3/5, y compris module uuid python , boost.uuid (C++) et SSI OSSP . (Je n'en ai pas cherché .net)

18
bacar

MD5 est faible, je pense que vous pouvez faire la même chose avec SHA-1 et obtenir de meilleurs résultats.

BTW, juste une opinion personnelle, habiller un hachage md5 comme un GUID ne fait pas un bon GUID. Les GUID de par leur nature sont non déterministes. Cela ressemble à une triche. Pourquoi pas juste appelez un chat un chat et dites simplement que c'est une chaîne de caractères hachée de l'entrée. vous pouvez le faire en utilisant cette ligne, plutôt que la nouvelle ligne de guidage:

string stringHash = BitConverter.ToString(hashBytes)
5
ryber

Vous devez faire une distinction entre les instances de la classe Guid et les identificateurs qui sont globalement uniques. Un "guid déterministe" est en fait un hachage (comme en témoigne votre appel à provider.ComputeHash). Les hachages ont un risque de collision beaucoup plus élevé (deux chaînes différentes produisant le même hachage) que Guid créé via Guid.NewGuid.

Le problème avec votre approche est donc que vous devrez accepter la possibilité que deux chemins différents produisent le même GUID. Si vous avez besoin d'un identifiant unique pour une chaîne de chemin d'accès donnée, la chose la plus simple à faire est d'utiliser simplement la chaîne . Si vous avez besoin que la chaîne soit cachée à vos utilisateurs, cryptez-la - vous pouvez utiliser ROT13 ou quelque chose de plus puissant ...

Tenter de chausse-pied quelque chose qui n'est pas un pur GUID dans le type de données GUID pourrait entraîner des problèmes de maintenance à l'avenir ...

3
Rob Fonseca-Ensor