如何创build确定性指导

在我们的应用程序中,我们正在创build具有Guid值的属性的Xml文件。 这个值需要在文件升级之间保持一致。 因此,即使文件中的其他内容发生更改,该属性的guid值也应该保持不变。

一个明显的解决scheme是创build一个带有文件名和Guid的静态字典用于他们。 然后,每当我们生成文件,我们查找字典的文件名,并使用相应的GUID。 但这是不可行的,因为我们可能会扩展到100个文件,并不想保留大量的guid。

所以另一种方法是使Guid基于文件的path相同。 由于我们的文件path和应用程序目录结构是唯一的,Guid应该是唯一的path。 所以每次我们运行升级时,文件都会根据path获取相同的GUID。 我发现了一个很酷的方法来生成这样的“ 决定性指导 ”(Elton Stoneman)。 它基本上这样做:

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

所以给一个string,Guid将永远是一样的。

有没有其他方法或build议的方法来做到这一点? 这种方法有什么优点或缺点?

如@ bacar所述, RFC 4122§4.3定义了一种创build基于名称的UUID的方法。 这样做的优点是(只使用MD5散列)是保证不会与非基于命名的UUID冲突,并且与其他基于名称的UUID发生冲突的可能性非常小。

在.NET Framework中没有创build这些的本机支持,但是我在GitHub上发布了实现algorithm的代码 。 它可以使用如下:

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

为了进一步降低与其他GUID冲突的风险,可以创build一个专用GUID作为名称空间ID(而不是使用RFC中定义的URL名称空间ID)。

这将把任何string转换成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,但这是一种将string数据密钥持续升级到Guid数据密钥的方法。

正如Rob提到的,你的方法不会生成一个UUID,它会生成一个看起来像UUID的哈希。

UUID上的RFC 4122特别允许确定性(基于名称的)UUID – 版本3和版本5分别使用md5和SHA1。 大多数人可能熟悉版本4,这是随机的。 维基百科给出了一个很好的版本的概述。 (请注意,在这里使用“版本”这个词似乎是描述UUID的“types” – 第5版不能代替版本4)。

似乎有一些用于生成版本3/5 UUID的库,包括python uuid模块 , boost.uuid (C ++)和OSSP UUID 。 (我没有找任何.net的)

MD5很弱,我相信你可以用SHA-1做同样的事情,并获得更好的结果。

顺便说一句,只是一个个人的意见,select一个GUID的MD5散列不会使它成为一个很好的GUID。 GUID的本质是非确定性的。 这感觉就像一个骗子。 为什么不直接调用一个铲子,只是说一个string呈现input的散列。 你可以通过使用这一行,而不是新的GUID行:

 string stringHash = BitConverter.ToString(hashBytes) 

您需要区分类Guid实例和全局唯一的标识符。 一个“确定性的guid”实际上是一个散列(如你对provider.ComputeHash的调用所certificate的)。 与通过Guid.NewGuid创build的Guid相比,哈希具有更高的碰撞几率(产生相同哈希的两个不同的string)。

所以你的方法的问题是你必须确定两个不同的path将产生相同的GUID的可能性。 如果您需要任何给定pathstring的唯一标识符,那么最简单的方法就是使用string 。 如果你需要把string从你的用户中隐藏起来,请将其encryption – 你可以使用ROT13或更强大的function。

试图在GUID数据types中使用不是纯粹的GUID的东西可能会导致将来的维护问题…