2010-04-15 24 views
84

En nuestra aplicación, creamos archivos Xml con un atributo que tiene un valor Guid. Este valor debe ser coherente entre las actualizaciones de archivos. Entonces, incluso si todo lo demás en el archivo cambia, el valor guid para el atributo debería permanecer igual.Cómo crear guías deterministas

Una solución obvia era crear un diccionario estático con el nombre de archivo y las guías que se usarían para ellos. Luego, cada vez que generamos el archivo, buscamos el diccionario para el nombre del archivo y usamos el guid correspondiente. Pero esto no es factible porque podríamos escalar a cientos de archivos y no queríamos mantener una gran lista de guías.

Así que otro enfoque fue hacer que el Guid sea el mismo basado en la ruta del archivo. Dado que nuestras rutas de archivos y la estructura del directorio de la aplicación son únicas, el Guid debe ser exclusivo para esa ruta. Entonces, cada vez que ejecutamos una actualización, el archivo obtiene el mismo guid en función de su ruta. Encontré una buena manera de generar tal 'Deterministic Guids' (Gracias Elton Stoneman). Básicamente hace esto:

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; 

} 

Por lo tanto, dado una cadena, el Guid siempre será el mismo.

¿Hay otros enfoques o formas recomendadas de hacerlo? ¿Cuáles son los pros o los contras de ese método?

Respuesta

121

Como lo menciona @bacar, RFC 4122 §4.3 define una forma de crear un UUID basado en nombre. La ventaja de hacer esto (más simplemente usando un hash MD5) es que estos se garantiza que no chocan con los UUID no basados ​​en el nombre, y tienen un muy (muy) pequeña posibilidad de colisión con otros UUID basados ​​en nombres.

No existe soporte nativo en .NET Framework para crear estos, pero publiqué code on GitHub que implementa el algoritmo. Puede ser utilizado de la siguiente manera:

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

Para reducir el riesgo de colisiones con otros GUID aún más, se podría crear un GUID privada para su uso como el ID de espacio de nombres (en lugar de utilizar el ID de URL espacio de nombres definido en el RFC)

+2

Estoy muy contento de haber encontrado esto, gracias por publicarlo. –

+0

Tenga en cuenta que si bien esto es útil, la implementación no consigue que RFC4122 sea correcto, por lo que si intenta ser compatible con otra implementación, tendrá problemas (pruebe el ejemplo en el código C en el apéndice de RFC). – porges

+4

@Porges: RFC4122 es incorrecto y tiene una errata que corrige el código C (http://www.rfc-editor.org/errata_search.php?rfc=4122&eid=1352). Si esta implementación no es totalmente compatible con RFC4122 y su errata, proporcione más detalles; Me gustaría hacer que siga el estándar. –

5

MD5 es débil, creo que puede hacer lo mismo con SHA-1 y obtener mejores resultados.

Por cierto, solo una opinión personal, vestir un hash md5 como un GUID no lo convierte en un buen GUID. Las GUID por su propia naturaleza no son deterministas. esto se siente como un truco. ¿Por qué no llamar simplemente a spade a spade y simplemente decir que es un hash prestado de la entrada? que podría hacer que el uso de esta línea, en lugar de la nueva línea GUID:

string stringHash = BitConverter.ToString(hashBytes) 
+0

Gracias por su entrada, pero esto todavía me da una cadena, y estoy buscando un GUID ... – desigeek

+0

, llame a su hash de un "GUID", Ok problema resuelto. ¿O es el verdadero problema que * necesitas * un objeto 'Guid'? – user7116

+0

desearía que fuera así de simple ... :) pero sí, necesito un objeto 'GUID' – desigeek

4

Usted necesita hacer una distinción entre instancias de la clase Guid e identificadores que son único a nivel mundial. Una "guía determinista" es en realidad un hash (como lo demuestra su llamada al provider.ComputeHash). Los hashes tienen una probabilidad mucho mayor de colisiones (dos cadenas diferentes que suceden para producir el mismo hash) que Guid creado a través del Guid.NewGuid.

Así que el problema con su enfoque es que tendrá que estar bien con la posibilidad de que dos rutas diferentes produzcan el mismo GUID. Si necesita un identificador único para una cadena de ruta dada, lo más fácil es , simplemente use la cadena. Si necesita la cadena que se oculta de sus usuarios, cifrarlo - se puede utilizar ROT13 o algo más potente ...

El intento de meter con calzador algo que no es un GUID puro en el tipo de datos GUID podría llevar a problemas de mantenimiento en el futuro ...

+0

Usted afirma que "Hashes tiene muchas más posibilidades de colisión ... que Guid creado a través de Guid.NewGuid". Puedes profundizar sobre eso? Desde un punto de vista matemático, el número de bits que uno puede establecer es el mismo, y tanto MD5 como SHA1 son hashes cifrados, diseñados específicamente para reducir la probabilidad de colisiones hash (accidentales e intencionales). – MarkusSchaber

17

Como Rob menciona, su método no genera un UUID, genera un hash que se parece a un UUID.

El RFC 4122 en UUID permite específicamente UUID deterministas (basados ​​en nombre) - Las versiones 3 y 5 usan md5 y SHA1 (respectivamente). La mayoría de la gente probablemente esté familiarizada con la versión 4, que es aleatoria. Wikipedia ofrece una buena visión general de las versiones. (Tenga en cuenta que el uso de la palabra 'versión' aquí parece describir un 'tipo' de UUID - la versión 5 no reemplaza a la versión 4).

Parece haber algunas bibliotecas para generar UUID de versión 3/5, incluidos python uuid module, boost.uuid (C++) y OSSP UUID. (No he buscado ningún .NET)

+1

Esto es exactamente lo que busca el póster original. UUID ya tiene un algoritmo para que pueda comenzar con una cadena y convertirla en un GUID. UUID versión 3 mezcla la cadena con MD5, mientras que la versión 5 la mezcla con SHA1. El punto importante al crear un "guid" es hacerlo "único" frente a otros GUID. El algoritmo define dos bits que se deben establecer, así como un nibble se establece en 3 o 5, dependiendo de si es la versión 3 o 5. –

+1

Con respecto al uso de la palabra "versión", RFC 4122 §4.1.3 establece: "La versión es más exactamente un subtipo, de nuevo, conservamos el término de compatibilidad". –

+9

Publiqué un código C# para crear GUID v3 y v5 en GitHub: https://github.com/LogosBible/Logos.Utility/blob/master/src/Logos.Utility/GuidUtility.cs –

25

Esto convertirá cualquier cadena en un Guid sin tener que importar un ensamblaje exterior.

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

hay mucho mejores maneras de generar un GUID único, pero esta es una manera de actualizar constantemente una clave de datos de cadena a una llave de datos GUID.

+0

Encontré este fragmento para ser útil cuando se usa un identificador único en una base de datos para la distribución federada. – Gleno

+2

¡Advertencia! Este código no genera guías/UUID válidos (como bacar también se menciona a continuación). Ni la versión ni el campo tipo están configurados correctamente. – MarkusSchaber

+3

¿No sería igual de eficaz usar MD5CryptoServiceProvider en lugar de SHA1, ya que MD5 ya tiene 16 bytes de longitud? – Brain2000

Cuestiones relacionadas