Как создать детерминированные гиды

В нашем приложении мы создаем файлы Xml с атрибутом, имеющим значение Guid. Это значение должно быть согласовано между обновлениями файлов. Поэтому, даже если все остальное в файле изменяется, значение guid для атрибута должно оставаться неизменным.

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

Таким образом, другой подход заключался в том, чтобы сделать 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 всегда будет одинаковой.

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

Ответ 1

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

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

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

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

Ответ 2

Это преобразует любую строку в 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.

Ответ 3

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

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

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

Ответ 4

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

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

string stringHash = BitConverter.ToString(hashBytes)

Ответ 5

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

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

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