.net 如何创建确定性参考线

7xzttuei  于 2023-03-31  发布在  .NET
关注(0)|答案(6)|浏览(116)

在我们的应用程序中,我们正在创建一个具有Guid值的属性的Xml文件。该值需要在文件升级之间保持一致。因此,即使文件中的其他内容发生更改,该属性的guid值也应该保持不变。
一个明显的解决方案是创建一个包含文件名和guid的静态字典。然后每当我们生成文件时,我们都会查找字典中的文件名并使用相应的guid。但这是不可行的,因为我们可能会扩展到100个文件,并且不想维护一个大的guid列表。
因此,另一种方法是根据文件的路径使Guid相同。由于我们的文件路径和应用程序目录结构是唯一的,因此Guid对于该路径应该是唯一的。因此,每次我们运行升级时,文件都会根据其路径获得相同的guid。我发现了一种很酷的方法来生成这样的' Deterministic Guids '(感谢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; 

}

因此,给定一个字符串,Guid将始终相同。
有没有其他方法或推荐的方法来做到这一点?这种方法的优点或缺点是什么?

col17t5w

col17t5w1#

正如@bacar所提到的,RFC 4122 §4.3定义了一种创建基于名称的UUID的方法。这样做的好处(相对于只使用MD5哈希)是保证这些UUID不会与非基于名称的UUID冲突,并且与其他基于名称的UUID冲突的可能性非常(非常)小。
在.NET Framework中没有创建这些的原生支持,但我发布了实现该算法的code on GitHub。它可以如下使用:

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

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

qkf9rpyu

qkf9rpyu2#

这会将任何字符串转换为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数据键的方法。

roejwanj

roejwanj3#

正如Rob提到的,您的方法不生成UUID,它生成一个看起来像UUID的散列。
关于UUID的RFC 4122特别允许确定性(基于名称)UUID-版本3和5分别使用md5和SHA1。大多数人可能熟悉版本4,它是随机的。Wikipedia给出了一个很好的版本概述。(注意,这里使用的单词“version”似乎描述了UUID的“type”-版本5并不取代版本4)。
似乎有几个库可以生成版本3/5的UUID,包括python uuid moduleboost.uuid(C++)和OSSP UUID(我没有寻找任何.net库)。

rqqzpn5f

rqqzpn5f4#

您需要区分类Guid的示例和全局唯一的标识符。“确定性guid”实际上是一个散列(正如您对provider.ComputeHash的调用所证明的那样)。散列比通过Guid.NewGuid创建的Guid有更高的冲突几率(两个不同的字符串碰巧产生相同的散列)。
所以你的方法的问题是,你将不得不接受两个不同的路径将产生相同的GUID的可能性。如果你需要一个标识符,对于任何给定的路径字符串是唯一的,那么最简单的事情就是 * 只使用字符串 *。如果你需要字符串从你的用户隐藏,加密它-你可以使用ROT 13或更强大的东西...
尝试将非纯GUID的内容硬塞进GUID数据类型中可能会导致将来的维护问题…

rt4zxlrg

rt4zxlrg5#

MD5很弱,我相信你可以用SHA-1做同样的事情,得到更好的结果。
顺便说一句,只是个人意见,将md5哈希作为GUID并不能使其成为一个好的GUID。GUID本身就是非确定性的。这感觉像是一个欺骗。为什么不直言不讳,只是说它是输入的字符串渲染哈希。你可以通过使用这行来做到这一点,而不是新的guid行:

string stringHash = BitConverter.ToString(hashBytes)
ipakzgxi

ipakzgxi6#

这里有一个非常简单的解决方案,对于单元/集成测试这样的事情来说应该足够好了:

var rnd = new Random(1234); // Seeded random number (deterministic).
Console.WriteLine($"{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}-{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}-{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}-{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}-{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}");

相关问题