Как создавать детерминированные направляющие - PullRequest
93 голосов
/ 15 апреля 2010

В нашем приложении мы создаем XML-файлы с атрибутом, который имеет значение Guid. Это значение должно быть согласованным между обновлениями файлов. Таким образом, даже если все остальное в файле изменяется, значение guid для атрибута должно оставаться неизменным.

Одним из очевидных решений было создание статического словаря с именем файла и направляющими для них. Затем всякий раз, когда мы генерируем файл, мы ищем словарь для имени файла и используем соответствующий guid. Но это неосуществимо, потому что мы могли бы масштабировать до сотен файлов и не хотели поддерживать большой список направляющих.

Таким образом, другой подход состоял в том, чтобы сделать Guid таким же, основываясь на пути к файлу. Поскольку наши пути к файлам и структура каталогов приложений уникальны, Guid должен быть уникальным для этого пути. Поэтому каждый раз, когда мы запускаем обновление, файл получает один и тот же guid в зависимости от своего пути. Я нашел один классный способ создать такие ' детерминированные направляющие ' (спасибо Элтону Стоунману). Это в основном делает это:

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; 

} 

Таким образом, учитывая строку, Guid всегда будет одинаковым.

Существуют ли другие подходы или рекомендуемые способы сделать это? Каковы плюсы или минусы этого метода?

Ответы [ 5 ]

139 голосов
/ 14 апреля 2011

Как упомянуто @bacar, RFC 4122 §4.3 определяет способ создания UUID на основе имени. Преимущество этого (по сравнению только с использованием хэша MD5) состоит в том, что они гарантированно не конфликтуют с UUID без имени и имеют очень (очень) небольшую возможность конфликта с другими UUID на основе имени.

В .NET Framework нет собственной поддержки для их создания, но я разместил код на GitHub , который реализует алгоритм. Может использоваться следующим образом:

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

Чтобы еще больше снизить риск коллизий с другими GUID, вы можете создать частный GUID для использования в качестве идентификатора пространства имен (вместо использования идентификатора пространства имен URL, определенного в RFC).

28 голосов
/ 22 февраля 2012

Это преобразует любую строку в Guid без необходимости импорта внешней сборки.

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

Существуют гораздо лучшие способы создания уникального Guid, но это способ последовательного обновления ключа строковых данных до ключа данных Guid.

18 голосов
/ 11 июня 2010

Как упоминает Роб, ваш метод не генерирует UUID, он генерирует хеш, который выглядит как UUID.

RFC 4122 для идентификаторов UUID специально позволяет использовать детерминированные (основанные на имени) идентификаторы UUID - в версиях 3 и 5 используются md5 и SHA1 (соответственно). Большинство людей, вероятно, знакомы с версией 4, которая является случайной. Википедия дает хороший обзор версий. (Обратите внимание, что использование слова «версия» здесь, по-видимому, описывает «тип» UUID - версия 5 не заменяет версию 4).

Кажется, есть несколько библиотек для генерации UUID версии 3/5, включая модуль python uuid , boost.uuid (C ++) и OSSP UUID . (Я не искал никаких .net)

5 голосов
/ 15 апреля 2010

MD5 слаб, я считаю, что вы можете сделать то же самое с SHA-1 и получить лучшие результаты.

Кстати, просто личное мнение, использование хеша md5 в качестве GUID не делает его хорошим GUID. GUID по своей природе не являются детерминированными. это похоже на чит. Почему бы просто не назвать вещи своими именами и просто сказать, что это строковый хэш ввода. Вы можете сделать это, используя эту строку, а не новую строку guid:

string stringHash = BitConverter.ToString(hashBytes)
3 голосов
/ 19 апреля 2010

Необходимо различать экземпляры класса Guid и идентификаторы, которые являются глобально уникальными. «Детерминированный гид» на самом деле является хешем (о чем свидетельствует ваш вызов provider.ComputeHash). Хэши имеют гораздо более высокую вероятность коллизий (две разные строки производят один и тот же хэш), чем Guid, созданный с помощью Guid.NewGuid.

Так что проблема с вашим подходом в том, что вы должны быть в порядке с возможностью того, что два разных пути будут давать один и тот же GUID. Если вам нужен идентификатор, который уникален для любой заданной строки пути, тогда проще всего сделать , просто использовать строку . Если вам нужно, чтобы строка была скрыта от ваших пользователей, зашифруйте ее - вы можете использовать ROT13 или что-то более мощное ...

Попытка вставить что-то, что не является чистым GUID, в тип данных GUID может привести к проблемам с обслуживанием в будущем ...

...