Cara Membuat Panduan Determinan

103

Dalam aplikasi kami, kami membuat file Xml dengan atribut yang memiliki nilai Guid. Nilai ini harus konsisten di antara peningkatan file. Jadi, meskipun semua hal lain di file berubah, nilai guid untuk atribut harus tetap sama.

Salah satu solusi yang jelas adalah membuat kamus statis dengan nama file dan Panduan yang akan digunakan untuk kamus tersebut. Kemudian setiap kali kami membuat file, kami mencari nama file di kamus dan menggunakan guid yang sesuai. Tapi ini tidak layak karena kami mungkin menskalakan hingga 100 file dan tidak ingin menyimpan daftar besar panduan.

Jadi pendekatan lain adalah membuat Panduan sama berdasarkan jalur file. Karena jalur file dan struktur direktori aplikasi kami unik, Panduan harus unik untuk jalur itu. Jadi setiap kali kita menjalankan peningkatan, file tersebut mendapatkan panduan yang sama berdasarkan jalurnya. Saya menemukan satu cara keren untuk menghasilkan 'Panduan Penentu ' seperti itu (Terima kasih Elton Stoneman). Ini pada dasarnya melakukan ini:

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; 

} 

Jadi jika diberi string, Panduannya akan selalu sama.

Apakah ada pendekatan lain atau cara yang direkomendasikan untuk melakukan ini? Apa pro atau kontra dari metode itu?

Punit Vora
sumber

Jawaban:

151

Seperti yang disebutkan oleh @bacar, RFC 4122 §4.3 mendefinisikan cara untuk membuat UUID berbasis nama. Keuntungan melakukan ini (dibandingkan hanya menggunakan hash MD5) adalah bahwa ini dijamin tidak akan bertabrakan dengan UUID berbasis non-nama, dan memiliki kemungkinan (sangat) kecil benturan dengan UUID berbasis nama lainnya.

Tidak ada dukungan asli dalam .NET Framework untuk membuatnya, tetapi saya memposting kode di GitHub yang menerapkan algoritme. Ini dapat digunakan sebagai berikut:

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

Untuk mengurangi risiko tabrakan dengan GUID lain lebih jauh, Anda dapat membuat GUID pribadi untuk digunakan sebagai ID namespace (daripada menggunakan ID namespace URL yang ditentukan di RFC).

Bradley Grainger
sumber
5
@Porges: RFC4122 salah dan memiliki errata yang memperbaiki kode C ( rfc-editor.org/errata_search.php?rfc=4122&eid=1352 ). Jika implementasi ini tidak sepenuhnya sesuai dengan RFC4122 dan errata-nya, berikan detail lebih lanjut; Saya ingin membuatnya mengikuti standar.
Bradley Grainger
1
@BradleyGrainger: Saya tidak menyadarinya, terima kasih / maaf! Saya harus selalu ingat untuk memeriksa errata saat membaca RFC ... :)
porges
3
@Porges: Sama-sama / tidak masalah. Ini mengejutkan pikiran bahwa mereka tidak memperbarui RFC di tempat dengan koreksi dari errata. Bahkan tautan di akhir dokumen akan jauh lebih membantu daripada mengandalkan pembaca untuk mengingat untuk mencari errata (semoga sebelum menulis implementasi berdasarkan RFC ...).
Bradley Grainger
1
@BradleyGrainger: jika Anda menggunakan versi HTML, ia memiliki tautan ke errata dari tajuk, misalnya tools.ietf.org/html/rfc4122 . Saya ingin tahu apakah ada ekstensi browser untuk selalu mengarahkan ke versi HTML ...
porges
2
Anda harus mempertimbangkan untuk berkontribusi ini ke .NET. Repo .NET ada di sini: github.com/dotnet/coreclr/tree/master/src/mscorlib/src/System
sapphiremirage
29

Ini akan mengubah string apa pun menjadi Guid tanpa harus mengimpor rakitan luar.

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

Ada cara yang jauh lebih baik untuk menghasilkan Panduan unik, tetapi ini adalah cara untuk meningkatkan data key string ke kunci data Guid secara konsisten.

Ben Gripka
sumber
Menemukan potongan ini berguna saat menggunakan pengenal unik dalam database untuk distribusi federasi.
Gleno
6
Peringatan! Kode ini tidak menghasilkan Panduan / UUID yang valid (seperti bacar yang juga disebutkan di bawah). Baik versi maupun bidang jenis tidak disetel dengan benar.
MarkusSchaber
3
Bukankah akan seefektif menggunakan MD5CryptoServiceProvider daripada SHA1, karena MD5 sudah memiliki panjang 16 byte?
Brain2000
20

Seperti yang disebutkan Rob, metode Anda tidak menghasilkan UUID, metode ini menghasilkan hash yang terlihat seperti UUID.

The RFC 4122 pada UUIDs khusus memungkinkan untuk deterministik (nama-based) UUIDs - Versi 3 dan 5 menggunakan md5 dan SHA1 (masing-masing). Kebanyakan orang mungkin akrab dengan versi 4, yang acak. Wikipedia memberikan gambaran yang bagus tentang versi-versi tersebut. (Perhatikan bahwa penggunaan kata 'versi' di sini sepertinya menggambarkan 'jenis' UUID - versi 5 tidak menggantikan versi 4).

Tampaknya ada beberapa pustaka di luar sana untuk menghasilkan UUID versi 3/5, termasuk modul python uuid , boost.uuid (C ++) dan OSSP UUID . (Saya belum mencari satu pun .net)

bacar
sumber
1
Inilah tepatnya yang diinginkan oleh poster asli. UUID sudah memiliki algoritme untuk Anda mulai dengan string dan mengubahnya menjadi GUID. UUID versi 3 meng-hash string dengan MD5, sedangkan versi 5 melakukan hash dengan SHA1. Poin penting dalam membuat "guid" adalah menjadikannya "unik" terhadap GUID lainnya. Algoritme menentukan dua bit yang harus disetel, serta satu nibble disetel ke 3 atau 5, tergantung apakah itu versi 3 atau 5.
Ian Boyd
2
Mengenai penggunaan kata "versi", RFC 4122 §4.1.3 menyatakan: "Versi tersebut lebih tepat merupakan sub-jenis; sekali lagi, kami mempertahankan istilah kompatibilitas."
Bradley Grainger
11
Saya memposting beberapa kode C # untuk membuat GUID v3 dan v5 di GitHub: github.com/LogosBible/Logos.Utility/blob/master/src/…
Bradley Grainger
@BradleyGrainger, saya mendapatkan Peringatan Bitwise-atau operator yang digunakan pada operan yang diperpanjang tanda; pertimbangkan untuk melakukan casting ke tipe unsigned yang lebih kecil terlebih dahulu
Sebastian
1
Ini keluar dari topik! Sarankan untuk memindahkan laporan bug lib individu ke GitHub.
bacar
3

Anda perlu membuat perbedaan antara instance kelas Guid, dan pengenal yang unik secara global. Sebuah "pedoman deterministik" sebenarnya adalah hash (sebagaimana dibuktikan dengan panggilan Anda untuk provider.ComputeHash). Hash memiliki peluang benturan yang jauh lebih tinggi (dua string berbeda terjadi untuk menghasilkan hash yang sama) daripada yang dibuat Guid melalui Guid.NewGuid.

Jadi masalah dengan pendekatan Anda adalah bahwa Anda harus baik-baik saja dengan kemungkinan bahwa dua jalur yang berbeda akan menghasilkan GUID yang sama. Jika Anda memerlukan pengenal yang unik untuk string jalur tertentu, hal termudah untuk dilakukan adalah menggunakan string tersebut . Jika Anda membutuhkan string untuk dikaburkan dari pengguna Anda, enkripsi - Anda dapat menggunakan ROT13 atau sesuatu yang lebih kuat ...

Mencoba untuk memasukkan sesuatu yang bukan GUID murni ke dalam tipe data GUID dapat menyebabkan masalah pemeliharaan di masa mendatang ...

Rob Fonseca-Ensor
sumber
2
Anda mengklaim "Hash memiliki peluang tabrakan yang jauh lebih tinggi ... daripada yang dibuat oleh Guid melalui Guid.NewGuid.". Dapatkah Anda menguraikan itu? Dari sudut pandang matematis, jumlah bit yang dapat disetel sama, dan MD5 dan SHA1 adalah hash kriptografis, yang dirancang khusus untuk menurunkan kemungkinan benturan hash (tidak disengaja dan disengaja).
MarkusSchaber
Saya akan mengatakan perbedaan utamanya adalah peta hash kriptografi dari satu ruang tak terbatas ke ruang tetap lainnya menggunakan sebuah fungsi. Pencitraan hash yang memetakan string panjang variabel ke 128 bit sedangkan Guid menghasilkan 128 bit pseudo-random. Pembangkitan pseudo-random tidak bergantung pada masukan awal melainkan dengan menghasilkan keluaran secara seragam di ruang keluaran menggunakan keacakan yang diunggulkan dari perangkat keras atau cara lain.
Thai Bui
2

MD5 lemah, saya yakin Anda dapat melakukan hal yang sama dengan SHA-1 dan mendapatkan hasil yang lebih baik.

BTW, hanya pendapat pribadi, mendandani hash md5 sebagai GUID tidak menjadikannya GUID yang baik. GUID pada dasarnya adalah non Deterministik. ini terasa seperti curang. Mengapa tidak memanggil sekop sekop dan katakan saja itu string yang diberikan hash dari input. Anda bisa melakukannya dengan menggunakan baris ini, bukan baris pedoman baru:

string stringHash = BitConverter.ToString(hashBytes)
ryber
sumber
Terima kasih atas masukan Anda, tetapi ini masih memberi saya string, dan saya mencari GUID ...
Punit Vora
Oke, panggil hash Anda sebagai "GUID", masalah terselesaikan. Ataukah sebenarnya masalah yang Anda butuhkan adalah sebuah Guidbenda?
pengguna7116
saya berharap sesederhana itu .. :) tapi ya, saya butuh objek 'GUID'
Punit Vora
5
"GUID pada dasarnya adalah non Deterministik" - ini hanya berlaku untuk jenis GUID tertentu ('versi'). Namun saya setuju bahwa "mendandani hash md5 sebagai GUID tidak membuat GUID yang baik" karena alasan lain seperti yang dijelaskan oleh @Bradley Grainger dan @Rob Fonseca-Ensor, dan jawaban saya atas pertanyaan ini.
bacar