Preciso de um identificador exclusivo no .NET (não é possível usar o GUID, pois é muito longo para este caso).
As pessoas acham que o algoritmo usado aqui é um bom candidato ou você tem alguma outra sugestão?
Preciso de um identificador exclusivo no .NET (não é possível usar o GUID, pois é muito longo para este caso).
As pessoas acham que o algoritmo usado aqui é um bom candidato ou você tem alguma outra sugestão?
Respostas:
Este é bom - http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx
e também aqui o GUID do YouTube
Você pode usar Base64:
string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
Isso gera uma string como E1HKfn68Pkms5zsZsvKONw ==. Como um GUID é sempre de 128 bits, você pode omitir o == que você sabe que sempre estará presente no final e que lhe dará uma string de 22 caracteres. Porém, não é tão curto quanto o YouTube.
Eu uso uma abordagem semelhante à de Dor Cohen, mas removendo alguns caracteres especiais:
var uid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "");
Isso produzirá apenas caracteres alfanuméricos. Não é garantido que os UIDs tenham sempre o mesmo comprimento. Aqui está um exemplo de execução:
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");
Mantenha uma data de referência (que neste caso é 1º de janeiro de 2016) a partir de quando você começará a gerar esses ids. Isso tornará seus ids menores.
Número gerado: 3af3c14996e54
milliseconds
é sempre 0 para esse DateTime
objeto
Pacote de uso simples. Eu o uso para gerador de ID de solicitação temporal.
https://www.nuget.org/packages/shortid
https://github.com/bolorundurowb/shortid
Usos System.Random
string id = ShortId.Generate();
// id = KXTR_VzGVUoOY
(da página do github)
Se você deseja controlar o tipo de id gerado especificando se deseja números, caracteres especiais e o comprimento, chame o método Generate e passe três parâmetros, o primeiro um booleano informando se deseja números, o segundo um booleano informando se deseja caracteres especiais, o último um número que indica sua preferência de comprimento.
string id = ShortId.Generate(true, false, 12);
// id = VvoCDPazES_w
Pelo que eu sei, apenas retirar uma parte de um GUID não é garantido como sendo único - na verdade, está longe de ser único.
A coisa mais curta que eu sei que garante exclusividade global é apresentada nesta postagem do blog de Jeff Atwood . Na postagem vinculada, ele discute várias maneiras de encurtar um GUID e, no final, reduz para 20 bytes por meio da codificação Ascii85 .
No entanto, se você precisa absolutamente de uma solução que não seja maior que 15 bytes, infelizmente você não tem outra escolha a não ser usar algo que não é garantido como único globalmente.
Os valores de IDENTITY devem ser únicos em um banco de dados, mas você deve estar ciente das limitações ... por exemplo, torna a inserção de dados em massa basicamente impossível, o que o tornará mais lento se estiver trabalhando com um número muito grande de registros.
Você também pode usar um valor de data / hora. Eu vi vários bancos de dados onde eles usam a data / hora para ser o PK e, embora não seja muito limpo - funciona. Se você controlar as inserções, poderá garantir com eficácia que os valores serão exclusivos no código.
Para meu aplicativo local, estou usando esta abordagem baseada no tempo:
/// <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;
}
aqui minha solução, não é segura para simultaneidade, não mais de 1000 GUIDs por segundos e 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;
}
código não otimizado, apenas uma amostra !.
UtcNow
retornará um valor de escala exclusivo para cada milissegundo: de acordo com as observações , a resolução depende do cronômetro do sistema. Além disso, é melhor garantir que o relógio do sistema não mude para trás! (Como a resposta do usuário 13971889 colocou essa pergunta no topo do meu feed e eu critiquei essa resposta, acho que devo repetir essa crítica aqui.)
Se seu aplicativo não tem alguns MILHÕES de pessoas, usando essa string única e curta no MESMO MILISEGUNDO, você pode pensar em usar a função abaixo.
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());
}
altere yyyy para yy se você só precisar usar seu aplicativo nos próximos 99 anos.
Atualização 20160511 : Função aleatória correta
- Adicionar objeto de bloqueio
- Mover variável aleatória para fora da função RandomString
Ref
lock
é permitir que você reutilize a mesma Random
instância. Acho que você se esqueceu de deletar essa linha!
Eu sei que está muito longe da data postada ... :)
Eu tenho um gerador que produz apenas 9 caracteres hexa, por exemplo: 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");
}
}
Com base na resposta de @ dorcohen e no comentário de @pootzko. Você pode usar isso. É seguro durante o fio.
var errorId = System.Web.HttpServerUtility.UrlTokenEncode(Guid.NewGuid().ToByteArray());
Jzhw2oVozkSNa2IkyK4ilA2
ou tente você mesmo em dotnetfiddle.net/VIrZ8j
Com base em alguns outros, aqui está minha solução que fornece um guid codificado diferente que é seguro para URL (e Docker) e não perde nenhuma informação:
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("=", "").Replace("+", "-").Replace("/", "_");
Os exemplos de saída são:
BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig
Em C # um long
valor possui 64 bits, que se codificado com Base64, haverá 12 caracteres, incluindo 1 preenchimento =
. Se cortarmos o preenchimento =
, haverá 11 caracteres.
Uma ideia maluca aqui é que poderíamos usar uma combinação de Unix Epoch e um contador para um valor de época para formar um long
valor. O Unix Epoch em C # DateTimeOffset.ToUnixEpochMilliseconds
está no long
formato, mas os primeiros 2 bytes dos 8 bytes são sempre 0, caso contrário, o valor de data e hora será maior que o valor máximo de data e hora. Isso nos dá 2 bytes para colocar um ushort
contador.
Portanto, no total, desde que o número de geração de IDs não exceda 65536 por milissegundo, podemos ter um ID único:
// 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)
}
Uso
Guid.NewGuid().ToTinyUuid()
Não é ciência do foguete converter de volta, então vou deixar você por isso.
Se você não precisa digitar a string, pode usar o seguinte:
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());
}
Isso converterá o Guid em uma string de 8 caracteres como esta:
{b77a49a5-182b-42fa-83a9-824ebd6ab58d} -> "䦥 띺 ᠫ 䋺 ꦃ ꦃ 亂 檽 趵"
{c5f8f7f5-8a7c-4511-b667-8ad36b446617} -> " 엸 詼 䔑 架 펊 䑫 ᝦ"
Este é o meu pequeno método para gerar um id único aleatório e curto. Usa um rng criptográfico para geração segura de números aleatórios. Adicione quaisquer caracteres que você precisa para a chars
string.
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);
}
para não perder caracteres (+/-) e se quiser usar seu guid em uma url, deve ser transformado em base32
para 10.000.000 nenhuma chave duplicada
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;
}
Você pode tentar a seguinte biblioteca:
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
retornará um valor de escala exclusivo para cada milissegundo: de acordo com as observações , a resolução depende do cronômetro do sistema. Além disso, é melhor garantir que o relógio do sistema não mude para trás! (a resposta de ur3an0 também tem esses problemas.)
você pode usar
code = await UserManager.GenerateChangePhoneNumberTokenAsync(input.UserId, input.MobileNumber);
seus 6
única agradáveis personagens, 599527
,143354
e quando o usuário virifica simplesmente
var result = await UserManager.VerifyChangePhoneNumberTokenAsync(input.UserId, input.Token, input.MobileNumber);
espero que isso ajude você
Guid.NewGuid().ToString().Split('-').First()
Eu uso Guid.NewGuid().ToString().Split('-')[0]
, ele obtém o primeiro item do array separado por '-'. É o suficiente para representar uma chave única.