.NET 短唯一标识符

发布于 2025-01-05 16:32:08 字数 211 浏览 2 评论 0 原文

我需要 .NET 中的唯一标识符(不能使用 GUID,因为对于这种情况来说它太长)。

人们是否认为此处使用的算法 是一个好的候选人或者您还有其他建议吗?

I need a unique identifier in .NET (cannot use GUID as it is too long for this case).

Do people think that the algorithm used here is a good candidate or do you have any other suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(23

调妓 2025-01-12 16:32:08

这是一个很好的 - http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friend-guid-in-c-sharp.aspx< /a>

还有这里
类似于 YouTube 的 GUID

您可以使用 Base64:

string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());

这会生成一个类似 E1HKfn68Pkms5zsZsvKONw== 的字符串。由于 GUID 是
总是 128 位,你可以省略你知道永远是的 ==
出现在最后,这会给你一个 22 个字符的字符串。这
不过不像 YouTube 那么短。

This one a good one - http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx

and also here
YouTube-like GUID

You could use Base64:

string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());

That generates a string like E1HKfn68Pkms5zsZsvKONw==. Since a GUID is
always 128 bits, you can omit the == that you know will always be
present at the end and that will give you a 22 character string. This
isn't as short as YouTube though.

与往事干杯 2025-01-12 16:32:08

我使用与 Dor Cohen 类似的方法,但删除了一些特殊字符:

var uid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "");     

这将仅输出字母数字字符。不保证 UID 的长度始终相同。这是一个示例运行:

vmKo0zws8k28fR4V4Hgmw 
TKbhS0G2V0KqtpHOU8e6Ug 
rfDi1RdO0aQHTosh9dVvw
3jhCD75fUWjQek8XRmMg 
CQUg1lXIXkWG8KDFy7z6Ow 
bvyxW5aj10OmKA5KMhppw
pIMK8eq5kyvLK67xtsIDg
VX4oljGWpkSQGR2OvGoOQ 
NOHBjUUHv06yIc7EvotRg
iMniAuUG9kiGLwBtBQByfg

I use a similar approach as Dor Cohen's but removing some special characters:

var uid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "");     

This will output just alphanumeric characters. The UIDs are not guaranteed to have always the same length. Here is a sample run:

vmKo0zws8k28fR4V4Hgmw 
TKbhS0G2V0KqtpHOU8e6Ug 
rfDi1RdO0aQHTosh9dVvw
3jhCD75fUWjQek8XRmMg 
CQUg1lXIXkWG8KDFy7z6Ow 
bvyxW5aj10OmKA5KMhppw
pIMK8eq5kyvLK67xtsIDg
VX4oljGWpkSQGR2OvGoOQ 
NOHBjUUHv06yIc7EvotRg
iMniAuUG9kiGLwBtBQByfg
差↓一点笑了 2025-01-12 16:32:08
var ticks = new DateTime(2016,1,1).Ticks;
var ans = DateTime.Now.Ticks - ticks;
var uniqueId = ans.ToString("x");

从开始生成这些 id 的时间起保留一个基准日期(在本例中为 2016 年 1 月 1 日)。这将使你的 id 更小。

生成的编号:3af3c14996e54

var ticks = new DateTime(2016,1,1).Ticks;
var ans = DateTime.Now.Ticks - ticks;
var uniqueId = ans.ToString("x");

Keep a baseline date (which in this case is 1st Jan 2016) from when you will start generating these ids. This will make your ids smaller.

Generated Number: 3af3c14996e54

ゝ杯具 2025-01-12 16:32:08

简单可用的包。我将它用于临时请求 id 生成器。

https://www.nuget.org/packages/shortid

https://github.com/bolorundurowb/shortid

使用 System.Random

string id = ShortId.Generate();
// id = KXTR_VzGVUoOY

(来自 github 页面)

如果您愿意控制类型通过指定是否需要数字、特殊字符和长度生成的id,调用Generate方法并传递三个参数,第一个布尔值表示是否想要数字,第二个布尔值表示是否想要特殊字符,最后一个数字表明您的长度偏好。

string id = ShortId.Generate(true, false, 12);
// id = VvoCDPazES_w

Simple usable package. I use it for temporal request id generator.

https://www.nuget.org/packages/shortid

https://github.com/bolorundurowb/shortid

Uses System.Random

string id = ShortId.Generate();
// id = KXTR_VzGVUoOY

(from the github page)

If you want to control the type of id generated by specifying whether you want numbers, special characters and the length, call the Generate method and pass three parameters, the first a boolean stating whether you want numbers, the second a boolean stating whether you want special characters, the last a number indicating your length preference.

string id = ShortId.Generate(true, false, 12);
// id = VvoCDPazES_w
柠栀 2025-01-12 16:32:08

以防万一,仅仅删除连字符就可以了:

Guid.NewGuid().ToString("n")

这会生成完全唯一的 32 个字符的字符串:

5db4cee3bfd8436395d37fca2d48d5b3
82fac271c76148a3a0667c00a5da990d

Just in case merely removing hyphens will do for anyone:

Guid.NewGuid().ToString("n")

This generates perfectly unique strings of 32 characters:

5db4cee3bfd8436395d37fca2d48d5b3
82fac271c76148a3a0667c00a5da990d
倾其所爱 2025-01-12 16:32:08

据我所知,只是剥离了一部分GUID 不能保证是唯一的 - 事实上,它远非唯一。

据我所知,保证全球唯一性的最短内容在 Jeff Atwood 的这篇博客文章中介绍< /a>.在链接的帖子中,他讨论了缩短 GUID 的多种方法,并最终通过 将其减少到 20 个字节Ascii85 编码

但是,如果您绝对需要一个不超过 15 字节的解决方案,恐怕您别无选择,只能使用不能保证全局唯一的东西。

As far as I know, just stripping off a part of a GUID isn't guaranteed to be unique - in fact, it's far from being unique.

The shortest thing that I know that guarantees global uniqueness is featured in this blog post by Jeff Atwood. In the linked post, he discusses multiple ways to shorten a GUID, and in the end gets it down to 20 bytes via Ascii85 encoding.

However, if you absolutely need a solution that's no longer than 15 bytes, I'm afraid you have no other choice than to use something which is not guaranteed to be globally unique.

奢华的一滴泪 2025-01-12 16:32:08

对于我的本地应用程序,我使用这种基于时间的方法:

/// <summary>
/// Returns all ticks, milliseconds or seconds since 1970.
/// 
/// 1 tick = 100 nanoseconds
/// 
/// Samples:
/// 
/// Return unit     value decimal           length      value hex       length
/// --------------------------------------------------------------------------
/// ticks           14094017407993061       17          3212786FA068F0  14
/// milliseconds    1409397614940           13          148271D0BC5     11
/// seconds         1409397492              10          5401D2AE        8
///
/// </summary>
public static string TickIdGet(bool getSecondsNotTicks, bool getMillisecondsNotTicks, bool getHexValue)
{
    string id = string.Empty;

    DateTime historicalDate = new DateTime(1970, 1, 1, 0, 0, 0);

    if (getSecondsNotTicks || getMillisecondsNotTicks)
    {
        TimeSpan spanTillNow = DateTime.UtcNow.Subtract(historicalDate);

        if (getSecondsNotTicks)
            id = String.Format("{0:0}", spanTillNow.TotalSeconds);
        else
            id = String.Format("{0:0}", spanTillNow.TotalMilliseconds);
    }
    else
    {
        long ticksTillNow = DateTime.UtcNow.Ticks - historicalDate.Ticks;
        id = ticksTillNow.ToString();
    }

    if (getHexValue)
        id = long.Parse(id).ToString("X");

    return id;
}

For my local app I'm using this time based approach:

/// <summary>
/// Returns all ticks, milliseconds or seconds since 1970.
/// 
/// 1 tick = 100 nanoseconds
/// 
/// Samples:
/// 
/// Return unit     value decimal           length      value hex       length
/// --------------------------------------------------------------------------
/// ticks           14094017407993061       17          3212786FA068F0  14
/// milliseconds    1409397614940           13          148271D0BC5     11
/// seconds         1409397492              10          5401D2AE        8
///
/// </summary>
public static string TickIdGet(bool getSecondsNotTicks, bool getMillisecondsNotTicks, bool getHexValue)
{
    string id = string.Empty;

    DateTime historicalDate = new DateTime(1970, 1, 1, 0, 0, 0);

    if (getSecondsNotTicks || getMillisecondsNotTicks)
    {
        TimeSpan spanTillNow = DateTime.UtcNow.Subtract(historicalDate);

        if (getSecondsNotTicks)
            id = String.Format("{0:0}", spanTillNow.TotalSeconds);
        else
            id = String.Format("{0:0}", spanTillNow.TotalMilliseconds);
    }
    else
    {
        long ticksTillNow = DateTime.UtcNow.Ticks - historicalDate.Ticks;
        id = ticksTillNow.ToString();
    }

    if (getHexValue)
        id = long.Parse(id).ToString("X");

    return id;
}
临走之时 2025-01-12 16:32:08

22 个字符,url 安全,并保留 Guid 唯一性。

// Our url safe, base 64 alphabet:
const string alphabet = "-_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

// Sanitized Guid string. Preserve the last two hex chars
var guidStr = "929F7C4D4B2644E1A122A379C02D6345";
var lastTwo = guidStr.Substring(30, 2);

string shortGuid = "";

// Iterate over the ten groups of 3 hex chars: 929 F7C 4D4 B26 44E 1A1 22A 379 C02 D63
for (var i = 0; i < 10; i++)
{
    var hex = guidStr.Substring(i*3, 3);              // Get the next 3 hex chars
    var x = Convert.ToInt32(hex, 16);                 // Convert to int
    shortGuid += $"{alphabet[x/64]}{alphabet[x%64]}"; // Lookup the two-digit base64 value
}
shortGuid += lastTwo; // Don't forget the last two

Console.WriteLine(shortGuid);

输出:

yDXWhiGAfc4v6EbTK0Px45

22 chars, url safe, and retains Guid uniqueness.

// Our url safe, base 64 alphabet:
const string alphabet = "-_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

// Sanitized Guid string. Preserve the last two hex chars
var guidStr = "929F7C4D4B2644E1A122A379C02D6345";
var lastTwo = guidStr.Substring(30, 2);

string shortGuid = "";

// Iterate over the ten groups of 3 hex chars: 929 F7C 4D4 B26 44E 1A1 22A 379 C02 D63
for (var i = 0; i < 10; i++)
{
    var hex = guidStr.Substring(i*3, 3);              // Get the next 3 hex chars
    var x = Convert.ToInt32(hex, 16);                 // Convert to int
    shortGuid += 
quot;{alphabet[x/64]}{alphabet[x%64]}"; // Lookup the two-digit base64 value
}
shortGuid += lastTwo; // Don't forget the last two

Console.WriteLine(shortGuid);

Output:

yDXWhiGAfc4v6EbTK0Px45
酷炫老祖宗 2025-01-12 16:32:08

IDENTITY 值在数据库中应该是唯一的,但您应该意识到其局限性...例如,它使得批量数据插入基本上不可能,如果您正在处理大量记录,这会减慢您的速度。

您也许还可以使用日期/时间值。我见过几个数据库,他们使用日期/时间作为 PK,虽然它不是超级干净,但它有效。如果控制插入,则可以有效保证代码中的值是唯一的。

IDENTITY values should be unique in a database, but you should be aware of the limitations... for example, it makes bulk data inserts basically impossible which will slow you down if you're working with a very large number of records.

You may also be able to use a date/time value. I've seen several databases where they use the date/time to be the PK, and while it's not super clean - it works. If you control the inserts, you can effectively guarantee that the values will be unique in code.

梦里的微风 2025-01-12 16:32:08

基于其他一些解决方案,这是我的解决方案,它提供了不同的编码 guid,它是 URL(和 Docker)安全的并且不会丢失任何信息:

Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("=", "").Replace("+", "-").Replace("/", "_");

示例输出是:

BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig

Based on some others, here is my solution which provides a different encoded guid which is URL (and Docker) safe and does not loose any information:

Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("=", "").Replace("+", "-").Replace("/", "_");

Example outputs are:

BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig
生活了然无味 2025-01-12 16:32:08
    public static string ToTinyUuid(this Guid guid)
    {
        return Convert.ToBase64String(guid.ToByteArray())[0..^2]  // remove trailing == padding 
            .Replace('+', '-')                          // escape (for filepath)
            .Replace('/', '_');                         // escape (for filepath)
    }

用法

Guid.NewGuid().ToTinyUuid()

转换回来并不是什么复杂的事情,所以我就先讲这么多了。

    public static string ToTinyUuid(this Guid guid)
    {
        return Convert.ToBase64String(guid.ToByteArray())[0..^2]  // remove trailing == padding 
            .Replace('+', '-')                          // escape (for filepath)
            .Replace('/', '_');                         // escape (for filepath)
    }

Usage

Guid.NewGuid().ToTinyUuid()

It's not rocket science to convert back, so I'll leave you that much.

乖不如嘢 2025-01-12 16:32:08

如果您的应用程序没有几百万人,使用它在同一毫秒生成简短的唯一字符串,您可以考虑使用以下函数。

private static readonly Object obj = new Object();
private static readonly Random random = new Random();
private string CreateShortUniqueString()
{
    string strDate = DateTime.Now.ToString("yyyyMMddhhmmssfff");
    string randomString ;
    lock (obj)
    {
        randomString = RandomString(3);
    }
    return strDate + randomString; // 16 charater
}
private string RandomString(int length)
{

    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxy";
    var random = new Random();
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
}

如果您只需要在下一个 99 年使用您的应用程序,请将 yyyy 更改为 yy。

更新20160511:正确的随机函数

- 添加锁定对象

- 将随机变量移出 RandomString 函数

参考

If your app dont have a few MILLIION people, using that generate short unique string at the SAME MILLISECOND, you can think about using below function.

private static readonly Object obj = new Object();
private static readonly Random random = new Random();
private string CreateShortUniqueString()
{
    string strDate = DateTime.Now.ToString("yyyyMMddhhmmssfff");
    string randomString ;
    lock (obj)
    {
        randomString = RandomString(3);
    }
    return strDate + randomString; // 16 charater
}
private string RandomString(int length)
{

    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxy";
    var random = new Random();
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
}

change yyyy to yy if you just need to use your app in next 99 year.

Update 20160511: Correct Random function

- Add Lock object

- Move random variable out of RandomString function

Ref

一瞬间的火花 2025-01-12 16:32:08

我的解决方案对于并发来说不安全,每秒不超过 1000 个 GUID,并且线程安全。

public static class Extensors
{

    private static object _lockGuidObject;

    public static string GetGuid()
    {

        if (_lockGuidObject == null)
            _lockGuidObject = new object();


        lock (_lockGuidObject)
        {

            Thread.Sleep(1);
            var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var epochLong = Convert.ToInt64((DateTime.UtcNow - epoch).TotalMilliseconds);

            return epochLong.DecimalToArbitrarySystem(36);

        }

    }

    /// <summary>
    /// Converts the given decimal number to the numeral system with the
    /// specified radix (in the range [2, 36]).
    /// </summary>
    /// <param name="decimalNumber">The number to convert.</param>
    /// <param name="radix">The radix of the destination numeral system (in the range [2, 36]).</param>
    /// <returns></returns>
    public static string DecimalToArbitrarySystem(this long decimalNumber, int radix)
    {
        const int BitsInLong = 64;
        const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        if (radix < 2 || radix > Digits.Length)
            throw new ArgumentException("The radix must be >= 2 and <= " + Digits.Length.ToString());

        if (decimalNumber == 0)
            return "0";

        int index = BitsInLong - 1;
        long currentNumber = Math.Abs(decimalNumber);
        char[] charArray = new char[BitsInLong];

        while (currentNumber != 0)
        {
            int remainder = (int)(currentNumber % radix);
            charArray[index--] = Digits[remainder];
            currentNumber = currentNumber / radix;
        }

        string result = new String(charArray, index + 1, BitsInLong - index - 1);
        if (decimalNumber < 0)
        {
            result = "-" + result;
        }

        return result;
    }

代码未优化,只是示例!。

here my solution, is not safe for concurrency, no more of 1000 GUID's per seconds and thread safe.

public static class Extensors
{

    private static object _lockGuidObject;

    public static string GetGuid()
    {

        if (_lockGuidObject == null)
            _lockGuidObject = new object();


        lock (_lockGuidObject)
        {

            Thread.Sleep(1);
            var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var epochLong = Convert.ToInt64((DateTime.UtcNow - epoch).TotalMilliseconds);

            return epochLong.DecimalToArbitrarySystem(36);

        }

    }

    /// <summary>
    /// Converts the given decimal number to the numeral system with the
    /// specified radix (in the range [2, 36]).
    /// </summary>
    /// <param name="decimalNumber">The number to convert.</param>
    /// <param name="radix">The radix of the destination numeral system (in the range [2, 36]).</param>
    /// <returns></returns>
    public static string DecimalToArbitrarySystem(this long decimalNumber, int radix)
    {
        const int BitsInLong = 64;
        const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        if (radix < 2 || radix > Digits.Length)
            throw new ArgumentException("The radix must be >= 2 and <= " + Digits.Length.ToString());

        if (decimalNumber == 0)
            return "0";

        int index = BitsInLong - 1;
        long currentNumber = Math.Abs(decimalNumber);
        char[] charArray = new char[BitsInLong];

        while (currentNumber != 0)
        {
            int remainder = (int)(currentNumber % radix);
            charArray[index--] = Digits[remainder];
            currentNumber = currentNumber / radix;
        }

        string result = new String(charArray, index + 1, BitsInLong - index - 1);
        if (decimalNumber < 0)
        {
            result = "-" + result;
        }

        return result;
    }

code not optimized, just sample!.

浅暮の光 2025-01-12 16:32:08

这是我生成随机且简短的唯一 ID 的小方法。使用加密 rng 来生成安全的随机数。将所需的任何字符添加到 chars 字符串中。

using System;
using System.Security.Cryptography;

// ...

private string GenerateRandomId(int length)
{
    string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    char[] outputChars = new char[length];
    
    using RandomNumberGenerator rng = RandomNumberGenerator.Create();
    int minIndex = 0;
    int maxIndexExclusive = charset.Length;
    int diff = maxIndexExclusive - minIndex;

    long upperBound = uint.MaxValue / diff * diff;

    byte[] randomBuffer = new byte[sizeof(int)];

    for (int i = 0; i < outputChars.Length; i++)
    {
        // Generate a fair, random number between minIndex and maxIndex
        uint randomUInt;
        do
        {
            rng.GetBytes(randomBuffer);
            randomUInt = BitConverter.ToUInt32(randomBuffer, 0);
        }
        while (randomUInt >= upperBound);
        int charIndex = (int)(randomUInt % diff);

        // Set output character based on random index
        outputChars[i] = charset[charIndex];
    }

    return new string(outputChars);
}

这是通过将随机整数缩小到字符集索引的范围来实现的,并通过重新滚动新整数来解决随机数是绝对上限的边缘情况。

该解决方案产生公平且均匀分布的输出,使用 1,000,000 个字符长的输出进行测试,显示没有明显的偏差:

string output = GenerateRandomId(1_000_000);
var tally = output.GroupBy(c => c).OrderBy(g => g.Key).Select(g => (g.Key, g.Count())).ToArray();

int average = (int)(tally.Aggregate(new BigInteger(0), (b, t) => {b += t.Item2; return b;}, b => b) / tally.Count());
int max = tally.Max(g => g.Item2);
int min = tally.Min(g => g.Item2);

Console.WriteLine($"Avg: {average}");
Console.WriteLine($"Max: {max}");
Console.WriteLine($"Min: {min}");


foreach((char key, int count) in tally) {
    Console.WriteLine($"{key}: {count}");
}

输出:

Avg: 27777
Max: 28163
Min: 27341
0: 28081
1: 27773
...
Z: 27725

Here's my small method to generate a random and short unique id. Uses a cryptographic rng for secure random number generation. Add whatever characters you need to the chars string.

using System;
using System.Security.Cryptography;

// ...

private string GenerateRandomId(int length)
{
    string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    char[] outputChars = new char[length];
    
    using RandomNumberGenerator rng = RandomNumberGenerator.Create();
    int minIndex = 0;
    int maxIndexExclusive = charset.Length;
    int diff = maxIndexExclusive - minIndex;

    long upperBound = uint.MaxValue / diff * diff;

    byte[] randomBuffer = new byte[sizeof(int)];

    for (int i = 0; i < outputChars.Length; i++)
    {
        // Generate a fair, random number between minIndex and maxIndex
        uint randomUInt;
        do
        {
            rng.GetBytes(randomBuffer);
            randomUInt = BitConverter.ToUInt32(randomBuffer, 0);
        }
        while (randomUInt >= upperBound);
        int charIndex = (int)(randomUInt % diff);

        // Set output character based on random index
        outputChars[i] = charset[charIndex];
    }

    return new string(outputChars);
}

This works by scaling a random integer down to the range of charset indices, and accounts for the edge case where the random number is the absolute upper bound by rerolling for a new integer.

This solution produces fair and evenly distributed output, tested with a 1,000,000 character long output showing no obvious biases:

string output = GenerateRandomId(1_000_000);
var tally = output.GroupBy(c => c).OrderBy(g => g.Key).Select(g => (g.Key, g.Count())).ToArray();

int average = (int)(tally.Aggregate(new BigInteger(0), (b, t) => {b += t.Item2; return b;}, b => b) / tally.Count());
int max = tally.Max(g => g.Item2);
int min = tally.Min(g => g.Item2);

Console.WriteLine(
quot;Avg: {average}");
Console.WriteLine(
quot;Max: {max}");
Console.WriteLine(
quot;Min: {min}");


foreach((char key, int count) in tally) {
    Console.WriteLine(
quot;{key}: {count}");
}

Output:

Avg: 27777
Max: 28163
Min: 27341
0: 28081
1: 27773
...
Z: 27725
谈场末日恋爱 2025-01-12 16:32:08

我知道它距离发布日期还很远...:)

我有一个生成器,它只生成 9 个十六进制字符,例如:C9D6F7FF3、C9D6FB52C

public class SlimHexIdGenerator : IIdGenerator
{
    private readonly DateTime _baseDate = new DateTime(2016, 1, 1);
    private readonly IDictionary<long, IList<long>> _cache = new Dictionary<long, IList<long>>();

    public string NewId()
    {
        var now = DateTime.Now.ToString("HHmmssfff");
        var daysDiff = (DateTime.Today - _baseDate).Days;
        var current = long.Parse(string.Format("{0}{1}", daysDiff, now));
        return IdGeneratorHelper.NewId(_cache, current);
    }
}


static class IdGeneratorHelper
{
    public static string NewId(IDictionary<long, IList<long>> cache, long current)
    {
        if (cache.Any() && cache.Keys.Max() < current)
        {
            cache.Clear();
        }

        if (!cache.Any())
        {
            cache.Add(current, new List<long>());
        }

        string secondPart;
        if (cache[current].Any())
        {
            var maxValue = cache[current].Max();
            cache[current].Add(maxValue + 1);
            secondPart = maxValue.ToString(CultureInfo.InvariantCulture);
        }
        else
        {
            cache[current].Add(0);
            secondPart = string.Empty;
        }

        var nextValueFormatted = string.Format("{0}{1}", current, secondPart);
        return UInt64.Parse(nextValueFormatted).ToString("X");
    }
}

I know it's quite far from posted date... :)

I have a generator which produces only 9 Hexa characters, eg: C9D6F7FF3, C9D6FB52C

public class SlimHexIdGenerator : IIdGenerator
{
    private readonly DateTime _baseDate = new DateTime(2016, 1, 1);
    private readonly IDictionary<long, IList<long>> _cache = new Dictionary<long, IList<long>>();

    public string NewId()
    {
        var now = DateTime.Now.ToString("HHmmssfff");
        var daysDiff = (DateTime.Today - _baseDate).Days;
        var current = long.Parse(string.Format("{0}{1}", daysDiff, now));
        return IdGeneratorHelper.NewId(_cache, current);
    }
}


static class IdGeneratorHelper
{
    public static string NewId(IDictionary<long, IList<long>> cache, long current)
    {
        if (cache.Any() && cache.Keys.Max() < current)
        {
            cache.Clear();
        }

        if (!cache.Any())
        {
            cache.Add(current, new List<long>());
        }

        string secondPart;
        if (cache[current].Any())
        {
            var maxValue = cache[current].Max();
            cache[current].Add(maxValue + 1);
            secondPart = maxValue.ToString(CultureInfo.InvariantCulture);
        }
        else
        {
            cache[current].Add(0);
            secondPart = string.Empty;
        }

        var nextValueFormatted = string.Format("{0}{1}", current, secondPart);
        return UInt64.Parse(nextValueFormatted).ToString("X");
    }
}
合久必婚 2025-01-12 16:32:08

基于@dorcohen 的回答和@pootzko 的评论。
你可以用这个。通过电线是安全的。

var errorId = System.Web.HttpServerUtility.UrlTokenEncode(Guid.NewGuid().ToByteArray());

Based on @dorcohen's answer and @pootzko's comment.
You can use this. It is safe over the wire.

var errorId = System.Web.HttpServerUtility.UrlTokenEncode(Guid.NewGuid().ToByteArray());
じее 2025-01-12 16:32:08

在 C# 中,long 值有 64 位,如果使用 Base64 编码,则将有 12 个字符,其中包括 1 个填充 =。如果我们修剪填充 =,则会有 11 个字符。

这里的一个疯狂想法是,我们可以使用 Unix Epoch 和一个纪元值的计数器的组合来形成一个 long 值。 C# DateTimeOffset.ToUnixEpochMilliseconds 中的 Unix Epoch 为 long 格式,但 8 个字节中的前 2 个字节始终为 0,因为否则日期时间值将大于最大日期时间值。这样我们就有了 2 个字节来放置一个 ushort 计数器。

因此,总的来说,只要 ID 生成的数量不超过每毫秒 65536 个,我们就可以拥有一个唯一的 ID:

// This is the counter for current epoch. Counter should reset in next millisecond
ushort currentCounter = 123;

var epoch = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
// Because epoch is 64bit long, so we should have 8 bytes
var epochBytes = BitConverter.GetBytes(epoch);
if (BitConverter.IsLittleEndian)
{
    // Use big endian
    epochBytes = epochBytes.Reverse().ToArray();
}

// The first two bytes are always 0, because if not, the DateTime.UtcNow is greater 
// than DateTime.Max, which is not possible
var counterBytes = BitConverter.GetBytes(currentCounter);
if (BitConverter.IsLittleEndian)
{
    // Use big endian
    counterBytes = counterBytes.Reverse().ToArray();
}

// Copy counter bytes to the first 2 bytes of the epoch bytes
Array.Copy(counterBytes, 0, epochBytes, 0, 2);

// Encode the byte array and trim padding '='
// e.g. AAsBcTCCVlg
var shortUid = Convert.ToBase64String(epochBytes).TrimEnd('=');

In C# a long value has 64 bits, which if encoded with Base64, there will be 12 characters, including 1 padding =. If we trim the padding =, there will be 11 characters.

One crazy idea here is we could use a combination of Unix Epoch and a counter for one epoch value to form a long value. The Unix Epoch in C# DateTimeOffset.ToUnixEpochMilliseconds is in long format, but the first 2 bytes of the 8 bytes are always 0, because otherwise the date time value will be greater than the maximum date time value. So that gives us 2 bytes to place an ushort counter in.

So, in total, as long as the number of ID generation does not exceed 65536 per millisecond, we can have an unique ID:

// This is the counter for current epoch. Counter should reset in next millisecond
ushort currentCounter = 123;

var epoch = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
// Because epoch is 64bit long, so we should have 8 bytes
var epochBytes = BitConverter.GetBytes(epoch);
if (BitConverter.IsLittleEndian)
{
    // Use big endian
    epochBytes = epochBytes.Reverse().ToArray();
}

// The first two bytes are always 0, because if not, the DateTime.UtcNow is greater 
// than DateTime.Max, which is not possible
var counterBytes = BitConverter.GetBytes(currentCounter);
if (BitConverter.IsLittleEndian)
{
    // Use big endian
    counterBytes = counterBytes.Reverse().ToArray();
}

// Copy counter bytes to the first 2 bytes of the epoch bytes
Array.Copy(counterBytes, 0, epochBytes, 0, 2);

// Encode the byte array and trim padding '='
// e.g. AAsBcTCCVlg
var shortUid = Convert.ToBase64String(epochBytes).TrimEnd('=');
风情万种。 2025-01-12 16:32:08

为了不丢失字符(+/-),并且如果您想在 url 中使用您的 guid,则必须将其转换为 base32,

以保证 10 000 000 无重复键

    public static List<string> guids = new List<string>();
    static void Main(string[] args)
    {
        for (int i = 0; i < 10000000; i++)
        {
            var guid = Guid.NewGuid();
            string encoded = BytesToBase32(guid.ToByteArray());
            guids.Add(encoded);
            Console.Write(".");
        }
        var result = guids.GroupBy(x => x)
                    .Where(group => group.Count() > 1)
                    .Select(group => group.Key);

        foreach (var res in result)
            Console.WriteLine($"Duplicate {res}");

        Console.WriteLine($"*********** end **************");
        Console.ReadLine();
    }

    public static string BytesToBase32(byte[] bytes)
    {
        const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        string output = "";
        for (int bitIndex = 0; bitIndex < bytes.Length * 8; bitIndex += 5)
        {
            int dualbyte = bytes[bitIndex / 8] << 8;
            if (bitIndex / 8 + 1 < bytes.Length)
                dualbyte |= bytes[bitIndex / 8 + 1];
            dualbyte = 0x1f & (dualbyte >> (16 - bitIndex % 8 - 5));
            output += alphabet[dualbyte];
        }

        return output;
    }

to not lose characters (+ / -) and if you want to use your guid in an url, it must be transformed into base32

for 10 000 000 no duplicate key

    public static List<string> guids = new List<string>();
    static void Main(string[] args)
    {
        for (int i = 0; i < 10000000; i++)
        {
            var guid = Guid.NewGuid();
            string encoded = BytesToBase32(guid.ToByteArray());
            guids.Add(encoded);
            Console.Write(".");
        }
        var result = guids.GroupBy(x => x)
                    .Where(group => group.Count() > 1)
                    .Select(group => group.Key);

        foreach (var res in result)
            Console.WriteLine($"Duplicate {res}");

        Console.WriteLine($"*********** end **************");
        Console.ReadLine();
    }

    public static string BytesToBase32(byte[] bytes)
    {
        const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        string output = "";
        for (int bitIndex = 0; bitIndex < bytes.Length * 8; bitIndex += 5)
        {
            int dualbyte = bytes[bitIndex / 8] << 8;
            if (bitIndex / 8 + 1 < bytes.Length)
                dualbyte |= bytes[bitIndex / 8 + 1];
            dualbyte = 0x1f & (dualbyte >> (16 - bitIndex % 8 - 5));
            output += alphabet[dualbyte];
        }

        return output;
    }
诗笺 2025-01-12 16:32:08

如果您不需要键入字符串,您可以使用以下命令:

static class GuidConverter
{
    public static string GuidToString(Guid g)
    {
        var bytes = g.ToByteArray();
        var sb = new StringBuilder();
        for (var j = 0; j < bytes.Length; j++)
        {
            var c = BitConverter.ToChar(bytes, j);
            sb.Append(c);
            j++;
        }
        return sb.ToString();
    }

    public static Guid StringToGuid(string s) 
        => new Guid(s.SelectMany(BitConverter.GetBytes).ToArray());
}

这会将 Guid 转换为 8 个字符的字符串,如下所示:

{b77a49a5-182b-42fa-83a9-824ebd6ab58d} --> “䦥띺ᠫ䋺ꦃ乱檽趵”

{c5f8f7f5-8a7c-4511-b667-8ad36b446617} --> "엸䔑架펊䑫ᝦ"

If you dont need to type the string you could use the following:

static class GuidConverter
{
    public static string GuidToString(Guid g)
    {
        var bytes = g.ToByteArray();
        var sb = new StringBuilder();
        for (var j = 0; j < bytes.Length; j++)
        {
            var c = BitConverter.ToChar(bytes, j);
            sb.Append(c);
            j++;
        }
        return sb.ToString();
    }

    public static Guid StringToGuid(string s) 
        => new Guid(s.SelectMany(BitConverter.GetBytes).ToArray());
}

This will convert the Guid to a 8 character String like this:

{b77a49a5-182b-42fa-83a9-824ebd6ab58d} --> "䦥띺ᠫ䋺ꦃ亂檽趵"

{c5f8f7f5-8a7c-4511-b667-8ad36b446617} --> "엸詼䔑架펊䑫ᝦ"

旧街凉风 2025-01-12 16:32:08
public static string Base36Guid() 
{
    byte[] bytes = Guid.NewGuid().ToByteArray();
    // Append a zero byte to ensure the number is interpreted as positive
    byte[] unsignedBytes = new byte[bytes.Length + 1];
    bytes.CopyTo(unsignedBytes, 0);
    BigInteger number = new BigInteger(unsignedBytes);

    const string base36Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    StringBuilder result = new StringBuilder();
    while (number > 0)
    {
        number = BigInteger.DivRem(number, 36, out BigInteger remainder);
        _ = result.Append(base36Chars[(int)remainder]);
    }
    return result.ToString();
}

根据需要调整基础和字符集。显然,基数越高,得到的字符串越短。由于使用了 Guid,这保证了全局唯一。

public static string Base36Guid() 
{
    byte[] bytes = Guid.NewGuid().ToByteArray();
    // Append a zero byte to ensure the number is interpreted as positive
    byte[] unsignedBytes = new byte[bytes.Length + 1];
    bytes.CopyTo(unsignedBytes, 0);
    BigInteger number = new BigInteger(unsignedBytes);

    const string base36Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    StringBuilder result = new StringBuilder();
    while (number > 0)
    {
        number = BigInteger.DivRem(number, 36, out BigInteger remainder);
        _ = result.Append(base36Chars[(int)remainder]);
    }
    return result.ToString();
}

Adjust base and charset as needed. Obviously, the higher the base the shorter the resulting string. This is guaranteed to be globally unique, due to the usage of Guid.

我做我的改变 2025-01-12 16:32:08
private static readonly object _getUniqueIdLock = new object();
public static string GetUniqueId()
{       
    lock(_getUniqueIdLock)
    {
        System.Threading.Thread.Sleep(1);
        return DateTime.UtcNow.Ticks.ToString("X");
    }
}
private static readonly object _getUniqueIdLock = new object();
public static string GetUniqueId()
{       
    lock(_getUniqueIdLock)
    {
        System.Threading.Thread.Sleep(1);
        return DateTime.UtcNow.Ticks.ToString("X");
    }
}
入画浅相思 2025-01-12 16:32:08

您只能使用

code = await UserManager.GenerateChangePhoneNumberTokenAsync(input.UserId, input.MobileNumber);

它的 6 个漂亮字符,599527143354

,当用户验证它时,

var result = await UserManager.VerifyChangePhoneNumberTokenAsync(input.UserId, input.Token, input.MobileNumber);

希望这对您有帮助

you can use

code = await UserManager.GenerateChangePhoneNumberTokenAsync(input.UserId, input.MobileNumber);

its 6 nice characters only, 599527 ,143354

and when user virify it simply

var result = await UserManager.VerifyChangePhoneNumberTokenAsync(input.UserId, input.Token, input.MobileNumber);

hope this help you

盛夏已如深秋| 2025-01-12 16:32:08
Guid.NewGuid().ToString().Split('-').First()
Guid.NewGuid().ToString().Split('-').First()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文