Jak tworzyć deterministyczne Guidy

W naszej aplikacji tworzymy pliki Xml z atrybutem, który ma wartość Guid. Ta wartość musiała być spójna pomiędzy aktualizacjami plików. Więc nawet jeśli Wszystko inne w pliku ulegnie zmianie, wartość guid dla atrybutu powinna pozostać taka sama.

Oczywistym rozwiązaniem było stworzenie statycznego słownika z nazwą pliku i identyfikatorami GUID, które mają być dla nich używane. Następnie za każdym razem, gdy generujemy plik, szukamy słownika dla nazwy pliku i używamy odpowiedniego guid. Ale to nie jest to możliwe, ponieważ możemy skalować do 100 plików i nie chcemy utrzymywać dużej listy GUID.

Więc innym podejściem było uczynienie Guid takim samym na podstawie ścieżki pliku. Ponieważ nasze ścieżki plików i struktura katalogów aplikacji są unikalne, Guid powinien być unikalny dla tej ścieżki. Więc za każdym razem, gdy uruchamiamy aktualizację, plik otrzymuje ten sam identyfikator guid na podstawie swojej ścieżki. Znalazłem jeden fajny sposób na wygenerowanie takich deterministycznych Guidów (dzięki Elton Stoneman). W zasadzie tak to:

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; 

} 

Więc biorąc pod uwagę ciąg znaków, Guid zawsze będzie taki sam.

Czy są jakieś inne podejścia lub zalecane sposoby, aby to zrobić? Jakie są plusy lub minusy tej metody?

Author: Charlie Salts, 2010-04-15

5 answers

Jak wspomniał @bacar, RFC 4122 §4.3 definiuje sposób tworzenia identyfikatora uuid opartego na nazwach. Zaletą tego działania (nad samym używaniem skrótu MD5) jest to, że mają one gwarancję, że nie będą kolidować z Uuidami bazującymi na nazwach i mają bardzo (bardzo) małą możliwość kolizji z innymi uuidami bazującymi na nazwach.

Nie ma natywnego wsparcia w. NET Framework do ich tworzenia, ale zamieściłem kod na GitHub , który implementuje algorytm. Może być stosowany jako "follows": {]}

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

Aby jeszcze bardziej zmniejszyć ryzyko kolizji z innymi identyfikatorami GUID, można utworzyć prywatny identyfikator GUID, który będzie używany jako ID przestrzeni nazw (zamiast używać identyfikatora URL zdefiniowanego w RFC).

 132
Author: Bradley Grainger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-14 01:15:30

Spowoduje to konwersję dowolnego ciągu znaków na identyfikator Guid bez konieczności importowania zewnętrznego zestawu.

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

Istnieją znacznie lepsze sposoby generowania unikalnego identyfikatora Guid, ale jest to sposób na konsekwentną aktualizację klucza danych string do klucza danych Guid.

 24
Author: Ben Gripka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-21 22:14:38

Jak wspomina Rob, twoja metoda nie generuje UUID, generuje hash, który wygląda jak UUID.

RFC 4122 na uuid pozwala na deterministyczne (oparte na nazwach) uuid-Wersje 3 i 5 używają odpowiednio md5 i SHA1. Większość ludzi prawdopodobnie zna wersję 4, która jest przypadkowa. Wikipedia daje dobry przegląd wersji. (Zauważ, że użycie słowa 'version' tutaj wydaje się opisywać 'Typ' UUID-wersja 5 nie zastępuje wersja 4).

Wydaje się, że istnieje kilka bibliotek do generowania uuid wersji 3/5, w tym moduł uuid Pythona , boost.uuid (C++) i OSSP uuid. (Nie szukałem żadnych. Net)

 18
Author: bacar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-11 14:09:18

MD5 jest słaby, wierzę, że można zrobić to samo z SHA-1 i uzyskać lepsze wyniki.

BTW, tylko osobista opinia, ubieranie hash md5 jako GUID nie czyni go dobrym GUID. Guidy ze swej natury nie są deterministyczne. to wygląda jak oszustwo. Dlaczego po prostu nie nazwać spade spade i po prostu powiedzieć, że jest to ciąg wyrenderowany hash wejścia. można to zrobić używając tej linii, a nie nowej linii guid:

string stringHash = BitConverter.ToString(hashBytes)
 5
Author: ryber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-15 02:28:20

Należy rozróżnić instancje klasy Guid i identyfikatory, które są unikalne globalnie. "Deterministyczny guid" jest w rzeczywistości skrótem (o czym świadczy Twoje wywołanie do provider.ComputeHash). Hasze mają znacznie większą szansę na kolizje (dwa różne łańcuchy wywołują ten sam hasz) niż Guid utworzony przez Guid.NewGuid.

Więc problem z twoim podejściem polega na tym, że będziesz musiał być ok z możliwością, że dwie różne ścieżki będą produkować ten sam GUID. Jeśli potrzebujesz identyfikator, który jest unikalny dla dowolnego ciągu ścieżki, wtedy najprostszą rzeczą do zrobienia jest po prostu użyj ciągu . Jeśli chcesz, aby ciąg został zasłonięty przed użytkownikami, zaszyfruj go - możesz użyć ROT13 lub czegoś mocniejszego...

[3]}próba przeniesienia czegoś, co nie jest czystym GUID do typu danych GUID może prowadzić do problemów konserwacyjnych w przyszłości...
 3
Author: Rob Fonseca-Ensor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-19 15:35:02