Tôi cần một số nhận dạng duy nhất trong .NET (không thể sử dụng GUID vì nó quá dài cho trường hợp này).
Mọi người có nghĩ rằng thuật toán được sử dụng ở đây là một ứng cử viên tốt hay bạn có bất kỳ đề xuất nào khác không?
Tôi cần một số nhận dạng duy nhất trong .NET (không thể sử dụng GUID vì nó quá dài cho trường hợp này).
Mọi người có nghĩ rằng thuật toán được sử dụng ở đây là một ứng cử viên tốt hay bạn có bất kỳ đề xuất nào khác không?
Câu trả lời:
Cái này là một cái hay - http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx
và cũng có ở đây GUID giống như YouTube
Bạn có thể sử dụng Base64:
string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
Điều đó tạo ra một chuỗi như E1HKfn68Pkms5zsZsvKONw ==. Vì GUID luôn là 128 bit, bạn có thể bỏ qua dấu == mà bạn biết sẽ luôn xuất hiện ở cuối và điều đó sẽ cung cấp cho bạn một chuỗi 22 ký tự. Tuy nhiên, nó không ngắn như YouTube.
Tôi sử dụng cách tiếp cận tương tự như của Dor Cohen nhưng loại bỏ một số ký tự đặc biệt:
var uid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "");
Điều này sẽ chỉ xuất ra các ký tự chữ và số. Các UID không được đảm bảo luôn có cùng độ dài. Đây là một cuộc chạy mẫu:
vmKo0zws8k28fR4V4Hgmw
TKbhS0G2V0KqtpHOU8e6Ug
rfDi1RdO0aQHTosh9dVvw
3jhCD75fUWjQek8XRmMg
CQUg1lXIXkWG8KDFy7z6Ow
bvyxW5aj10OmKA5KMhppw
pIMK8eq5kyvLK67xtsIDg
VX4oljGWpkSQGR2OvGoOQ
NOHBjUUHv06yIc7EvotRg
iMniAuUG9kiGLwBtBQByfg
var ticks = new DateTime(2016,1,1).Ticks;
var ans = DateTime.Now.Ticks - ticks;
var uniqueId = ans.ToString("x");
Giữ một ngày cơ sở (trong trường hợp này là ngày 1 tháng 1 năm 2016) từ khi bạn bắt đầu tạo các id này. Điều này sẽ làm cho id của bạn nhỏ hơn.
Số đã tạo: 3af3c14996e54
milliseconds
luôn là 0 cho DateTime
đối tượng đó
Gói sử dụng đơn giản. Tôi sử dụng nó cho trình tạo id yêu cầu tạm thời.
https://www.nuget.org/packages/shortid
https://github.com/bolorundurowb/shortid
Sử dụng System.Random
string id = ShortId.Generate();
// id = KXTR_VzGVUoOY
(từ trang github)
Nếu bạn muốn kiểm soát loại id được tạo bằng cách chỉ định xem bạn có muốn số, ký tự đặc biệt và độ dài hay không, hãy gọi phương thức Tạo và chuyển ba tham số, tham số đầu tiên là boolean cho biết bạn có muốn số hay không, tham số thứ hai là boolean cho biết bạn có muốn ký tự đặc biệt, số cuối cùng cho biết tùy chọn độ dài của bạn.
string id = ShortId.Generate(true, false, 12);
// id = VvoCDPazES_w
Theo như tôi biết, chỉ cần loại bỏ một phần của GUID không được đảm bảo là duy nhất - trên thực tế, nó còn lâu mới là duy nhất.
Điều ngắn gọn nhất mà tôi biết đảm bảo tính độc nhất toàn cầu được nêu trong bài đăng trên blog này của Jeff Atwood . Trong bài đăng được liên kết, anh ấy thảo luận về nhiều cách để rút ngắn GUID và cuối cùng, nó giảm xuống còn 20 byte thông qua mã hóa Ascii85 .
Tuy nhiên, nếu bạn thực sự cần một giải pháp không dài hơn 15 byte, tôi e rằng bạn không có lựa chọn nào khác ngoài việc sử dụng thứ gì đó không được đảm bảo là duy nhất trên toàn cầu.
Giá trị IDENTITY phải là duy nhất trong cơ sở dữ liệu, nhưng bạn nên biết các hạn chế ... ví dụ, nó khiến việc chèn dữ liệu hàng loạt về cơ bản là không thể, điều này sẽ làm chậm bạn nếu bạn đang làm việc với một số lượng rất lớn bản ghi.
Bạn cũng có thể sử dụng giá trị ngày / giờ. Tôi đã thấy một số cơ sở dữ liệu nơi họ sử dụng ngày / giờ để làm PK, và mặc dù nó không quá sạch nhưng nó hoạt động. Nếu bạn kiểm soát các phần chèn, bạn có thể đảm bảo hiệu quả rằng các giá trị sẽ là duy nhất trong mã.
Đối với ứng dụng địa phương của tôi, tôi đang sử dụng phương pháp dựa trên thời gian này:
/// <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;
}
đây là giải pháp của tôi, không an toàn cho đồng thời, không quá 1000 GUID mỗi giây và an toàn cho chuỗi.
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;
}
mã không được tối ưu hóa, chỉ là mẫu !.
UtcNow
trả về giá trị đánh dấu duy nhất cho mỗi mili giây: theo nhận xét , độ phân giải phụ thuộc vào bộ đếm thời gian hệ thống. Ngoài ra, bạn nên đảm bảo rằng đồng hồ hệ thống không thay đổi ngược lại! (Vì câu trả lời của người dùng13971889 đưa câu hỏi này lên đầu nguồn cấp dữ liệu của tôi và tôi đã phê bình câu trả lời đó, tôi nghĩ mình nên lặp lại lời chỉ trích đó ở đây.)
Nếu ứng dụng của bạn không có vài TRIỆU người, bằng cách sử dụng tạo chuỗi ngắn duy nhất với mức CÙNG TRIỆU GIÂY, bạn có thể nghĩ đến việc sử dụng hàm bên dưới.
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());
}
đổi yyyy thành yy nếu bạn chỉ cần sử dụng ứng dụng của mình trong 99 năm tới.
Cập nhật 20160511 : Đúng chức năng Ngẫu nhiên
- Thêm đối tượng Khóa
- Di chuyển biến ngẫu nhiên ra khỏi hàm RandomString
Tham khảo
lock
là để cho phép bạn sử dụng lại cùng một Random
phiên bản. Tôi nghĩ bạn đã quên xóa dòng đó!
Tôi biết nó còn khá xa so với ngày đăng ... :)
Tôi có một máy phát điện chỉ sản xuất 9 ký tự Hexa , ví dụ: 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");
}
}
Dựa trên câu trả lời của @ dorcohen và bình luận của @ pootzko. Bạn có thể sử dụng cái này. Nó là an toàn qua dây.
var errorId = System.Web.HttpServerUtility.UrlTokenEncode(Guid.NewGuid().ToByteArray());
Jzhw2oVozkSNa2IkyK4ilA2
hoặc thử mình ở dotnetfiddle.net/VIrZ8j
Dựa trên một số giải pháp khác, đây là giải pháp của tôi cung cấp một hướng dẫn được mã hóa khác là URL (và Docker) an toàn và không làm mất bất kỳ thông tin nào:
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("=", "").Replace("+", "-").Replace("/", "_");
Kết quả đầu ra ví dụ là:
BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig
Trong C #, một long
giá trị có 64 bit, nếu mã hóa bằng Base64 sẽ có 12 ký tự, trong đó có 1 phần đệm =
. Nếu chúng ta cắt bỏ phần đệm =
, sẽ có 11 ký tự.
Một ý tưởng điên rồ ở đây là chúng ta có thể sử dụng kết hợp Unix Epoch và bộ đếm cho một giá trị kỷ nguyên để tạo thành một long
giá trị. Unix Epoch trong C # DateTimeOffset.ToUnixEpochMilliseconds
có long
định dạng, nhưng 2 byte đầu tiên trong số 8 byte luôn là 0, vì nếu không giá trị ngày giờ sẽ lớn hơn giá trị thời gian ngày tối đa. Vì vậy, điều đó cho chúng ta 2 byte để đặt một bộ ushort
đếm vào.
Vì vậy, tổng cộng, miễn là số lần tạo ID không vượt quá 65536 mỗi mili giây, chúng tôi có thể có một ID duy nhất:
// 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('=');
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)
}
Sử dụng
Guid.NewGuid().ToTinyUuid()
Nó không phải là khoa học tên lửa để chuyển đổi trở lại, vì vậy tôi sẽ để lại cho bạn nhiều như vậy.
Nếu bạn không cần nhập chuỗi, bạn có thể sử dụng như sau:
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());
}
Điều này sẽ chuyển hướng dẫn thành một chuỗi 8 ký tự như sau:
{b77a49a5-182b-42fa-83a9-824ebd6ab58d} -> "䦥 띺 ᠫ 䋺 ꦃ 亂 檽 趵"
{c5f8f7f5-8a7c-4511-b667-8ad36b446617} -> " 엸 詼 䔑 架 펊 䑫 ᝦ"
Đây là phương pháp nhỏ của tôi để tạo một id duy nhất ngẫu nhiên và ngắn gọn. Sử dụng rng mật mã để tạo số ngẫu nhiên an toàn. Thêm bất kỳ ký tự nào bạn cần vào chars
chuỗi.
private string GenerateRandomId(int length)
{
char[] stringChars = new char[length];
byte[] randomBytes = new byte[length];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomBytes);
}
string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[randomBytes[i] % chars.Length];
}
return new string(stringChars);
}
để không bị mất các ký tự (+ / -) và nếu bạn muốn sử dụng hướng dẫn của mình trong một url, nó phải được chuyển đổi thành base32
với giá 10 000 000 không có khóa trùng lặp
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;
}
Bạn có thể thử với thư viện sau:
private static readonly object _getUniqueIdLock = new object();
public static string GetUniqueId()
{
lock(_getUniqueIdLock)
{
System.Threading.Thread.Sleep(1);
return DateTime.UtcNow.Ticks.ToString("X");
}
}
UtcNow
trả về giá trị đánh dấu duy nhất cho mỗi mili giây: theo nhận xét , độ phân giải phụ thuộc vào bộ đếm thời gian hệ thống. Ngoài ra, bạn nên đảm bảo rằng đồng hồ hệ thống không thay đổi ngược lại! (Câu trả lời của ur3an0 cũng có những vấn đề này.)
bạn có thể dùng
code = await UserManager.GenerateChangePhoneNumberTokenAsync(input.UserId, input.MobileNumber);
6
chỉ những ký tự đẹp của nó 599527
,143354
và khi người dùng xác minh nó đơn giản
var result = await UserManager.VerifyChangePhoneNumberTokenAsync(input.UserId, input.Token, input.MobileNumber);
hy vọng điều này sẽ giúp bạn
Guid.NewGuid().ToString().Split('-').First()
Tôi sử dụng Guid.NewGuid().ToString().Split('-')[0]
, nó nhận mục đầu tiên từ mảng được phân tách bằng dấu '-'. Nó đủ để đại diện cho một khóa duy nhất.