1
votes

Convertir une chaîne Id en Guid unique (ou de md5 en Guid)?

Je voudrais créer un système pour convertir un identifiant existant (identifiant entier ou identifiant de chaîne personnalisé)

Je voudrais créer une méthode d'assistance ou d'extension qui génère un Guid à partir de n'importe quelle valeur int, longue ou chaîne. L'idée est de mettre à jour une base de données tout en gardant un certain suivi de mon ancienne base de données. Chaque fois que je convertis un identifiant de chaîne comme "O-2019-10-15", le système génère le même Guid unique. Concentrons-nous sur la chaîne ici.

    public static Guid GenerateGuid(string input)
    {
        // Convertion
        byte[] _byteIds = Encoding.UTF8.GetBytes(input);

        //What about using MD5?
        MD5CryptoServiceProvider _md5 = new MD5CryptoServiceProvider();
        byte[] _checksum = _md5.ComputeHash(_byteIds);

        // Convert ?
        string part1 = /* ??? */;
        string part2 = /* ??? */;
        string part3 = /* ??? */;
        string part4 = /* ??? */;
        string part5 = /* ??? */;

        //Concat these 4 part into one string
        return Guid.Parse("{0}-{1}-{2}-{3}-{4}", part1, part2, part3, part4, part5);
    }

Que pensez-vous? Est-ce que md5 est un bon début? Y a-t-il une règle dans la représentation Guid ()?

L'idée de md5 est que je peux tout convertir en une signature de 16 octets. De là, j'ai juste besoin de le convertir en Guid (). Mais je ne connais pas les détails sur le Guid. Existe-t-il déjà des règles, une position réservée pour des données ou d'autres informations?


11 commentaires

Je ne peux pas penser à un moyen raisonnable de générer une valeur de clé primaire à partir d'une autre valeur de clé primaire. Habituellement, vous importez simplement et laissez la base de données cible se charger de la création de cette valeur automatique. Quant au hachage, ce sera un article séparé.


Il semble que Guid et MD5 font tous les deux 16 octets


Avec chaque fonction de hachage, vous avez intrinsèquement le risque de colissions de hachage - deux entrées entraînant la même sortie. Lorsque vous utilisez un hachage à des fins de comparaison, vous ne pouvez l'utiliser que pour un filtrage rapide "pas le même". Même si deux hachages correspondent, vous devez faire une comparaison complète pour vérifier qu'il ne s'agit pas simplement d'une colission de hachage. Étant donné que les fonctions de hachage ont une entrée variable, mais une longueur de sortie fixe, il n'y a aucun moyen d'éviter cela. Vous devrez donc faire face à des colissions.


Vous ne pourrez pas garantir l'unicité avec cette approche. Je ne sais pas pourquoi vous n'utilisez simplement pas Guid.NewGuid ()


@Christopher "Avec chaque fonction de hachage, vous avez intrinsèquement le danger de collisions de hachage". Oui je sais. Même chose avec le Guid.NewGuid () en fait. Mais c'est un risque que je peux prendre. Merci pour votre commentaire.


@MickyD J'utilise Guid.NewGuid (). Ma question est de savoir si vous ne pouvez pas utiliser Guid.NewGuid () et que vous devez générer le Guid à partir d'une chaîne ou d'une valeur md5, y a-t-il une règle pour le faire.


Et si vous pensez tous que je ne devrais pas générer manuellement un Guid comme celui-ci. Pourquoi? c'est en fait le point le plus important. Pourquoi. Seul le risque de collision? Si ce n'est que cela, ce n'est pas une bonne raison. Chacun peut évaluer le risque de son projet.


@BastienVandamme: Parce que la création manuelle a tendance à gâcher la partie Unique de "Global Unique IDentifier". Si vous voulez juste une certaine taille entière, c'est faisable. Mais cet int ne sera pas unique. À la fin de la journée, il semble que vous vouliez un simple entier. Pas un entier avec des propriétés secondaires spécifiques.


Cela n'a guère de sens. Je peux commencer avec une chaîne comme "ABC", prendre son hachage SHA-1 et le convertir comme par magie en 160 bits. Mais, dans la vraie vie, il a seulement autant d'entropie qu'une chaîne de trois caractères (peut-être 15 bits). Il n'y a aucun moyen que ce soit "unique au monde". Oui, vous pouvez prendre 120 bits et les écraser dans l'un des schémas GUID. Mais ce n'est toujours qu'un tas de bits avec peu d'entropie et ce n'est certainement pas un GUID


Selon la documentation sur Wikipedia et autres blogs depuis la version 4 il n'y a aucune garantie de pièce Unique et le risque de collision est pris en compte. Non, bien sûr, je ne convertirai jamais une chaîne comme ABC en Guid mais je peux traduire une chaîne plus sophistiquée en Guid. Ce que je veux, c'est la garantie d'unicité dans mon environnement. Bien sûr, je dois préfixer toute ma chaîne avec une sorte d'espace de noms.


Pas de problème, je pensais juste que vous recherchiez l'unicité. Merci pour la clarification :)


3 Réponses :


2
votes

Je ne ferais pas ça comme ça.

J'utiliserais Guid.NewGuid () pour le nouvel identifiant, puis je conserverais l'ancien identifiant à côté (ou dans une table de traduction).

La prochaine fois que j'aurai besoin du nouvel identifiant, je chercherai l'ancien identifiant et verrai si j'ai déjà un guide pour cela.


S'il est essentiel de conserver un identifiant, ce que je ne recommande pas, je l'aurais comme $ "{guid} + {oldid}" .


1 commentaires

C'est une bonne pratique mais cela ne répond pas à la question ;-)



1
votes

Pour le moment, j'ai fait ceci

Guid GenerateGuid(string input)
{
    byte[] _byteIds = Encoding.UTF8.GetBytes(input);

    MD5CryptoServiceProvider _md5 = new MD5CryptoServiceProvider();
    byte[] _checksum = _md5.ComputeHash(_byteIds);

    //Convert checksum into 4 ulong parts and use BASE36 to encode both
    string part1 = BitConverter.ToString(_checksum, 0, 4).Replace("-", string.Empty);
    string part2 = BitConverter.ToString(_checksum, 4, 2).Replace("-", string.Empty);
    string part3 = BitConverter.ToString(_checksum, 6, 2).Replace("-", string.Empty);
    string part4 = BitConverter.ToString(_checksum, 8, 2).Replace("-", string.Empty);
    string part5 = BitConverter.ToString(_checksum, 10, 6).Replace("-", string.Empty);

    return Guid.Parse($"{part1}-{part2}-{part3}-{part4}-{part5}");
}

Pour éviter les collisions, l'entrée doit également être unique dans mon environnement. Je vais le préfixer avec un espace de nom.


1 commentaires

Pourquoi utiliser Guid.Parse , alors que vous pourriez utiliser le constructeur Guid qui prend un tableau d'octets new Guid (_md5.ComputeHash (_byteIds));



0
votes

Créer des UUID déterministes basés sur un espace de noms existant est exactement ce à quoi les UUIDv3 / v5 sont destinés. Cependant, vous aurez d'abord besoin d'un UUID d'espace de noms.

Les espaces de noms hiérarchiques constituent un moyen pratique (et valide) d'y parvenir. Tout d'abord, utilisez l'UUID d'espace de noms DNS standard plus votre nom de domaine pour générer votre espace de noms racine:

Guid nsDNS = nouveau Guid ("6ba7b810-9dad-11d1-80b4-00c04fd430c8");

Guid nsRoot = Guid.Create (nsDNS, "myapp.example.com", 5);

Ensuite, créez un UUID d'espace de noms pour votre chaîne:

Guid nsFoo = Guid.Create (nsRoot, "Foo", 5);

Vous êtes maintenant prêt à utiliser votre nouvel UUID d'espace de noms Foo avec des noms individuels:

Guid bar = Guid.Create (nsFoo, "Bar", 5);

L'avantage de ceci est que n'importe qui d'autre obtiendra des UUID complètement différents de vous, même si leurs chaînes (autres que le domaine, évidemment) sont identiques aux vôtres, évitant ainsi les collisions si vos ensembles de données sont fusionnés, mais c'est complètement déterministe , logique et auto-documentée.

(Remarque: je n'ai jamais utilisé C #, donc si la syntaxe est légèrement erronée, n'hésitez pas à la modifier. Je pense que le modèle est clair malgré tout.)


0 commentaires