Necesito un identificador único en .NET (no puedo usar GUID porque es demasiado largo para este caso).
¿Piensa la gente que el algoritmo utilizado aquí es un buen candidato o tiene alguna otra sugerencia?
Necesito un identificador único en .NET (no puedo usar GUID porque es demasiado largo para este caso).
¿Piensa la gente que el algoritmo utilizado aquí es un buen candidato o tiene alguna otra sugerencia?
Respuestas:
Este es uno bueno: http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx
y también aquí GUID similar a YouTube
Podrías usar Base64:
string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
Eso genera una cadena como E1HKfn68Pkms5zsZsvKONw ==. Dado que un GUID es siempre de 128 bits, puede omitir el == que sabe que siempre estará presente al final y que le dará una cadena de 22 caracteres. Sin embargo, esto no es tan corto como YouTube.
Utilizo un enfoque similar al de Dor Cohen pero eliminando algunos caracteres especiales:
var uid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "");
Esto generará solo caracteres alfanuméricos. No se garantiza que los UID tengan siempre la misma longitud. Aquí hay una muestra de ejecución:
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");
Mantenga una fecha de referencia (que en este caso es el 1 de enero de 2016) a partir de la cual comenzará a generar estos identificadores. Esto hará que sus identificadores sean más pequeños.
Número generado: 3af3c14996e54
milliseconds
es siempre 0 para ese DateTime
objeto
Paquete utilizable simple. Lo uso para el generador de ID de solicitud temporal.
https://www.nuget.org/packages/shortid
https://github.com/bolorundurowb/shortid
Usos System.Random
string id = ShortId.Generate();
// id = KXTR_VzGVUoOY
(de la página de github)
Si desea controlar el tipo de identificación generada especificando si desea números, caracteres especiales y la longitud, llame al método Generate y pase tres parámetros, el primero un booleano que indique si desea números, el segundo un booleano que indique si desea caracteres especiales, el último un número que indica su preferencia de longitud.
string id = ShortId.Generate(true, false, 12);
// id = VvoCDPazES_w
Hasta donde yo sé, no se garantiza que simplemente eliminar una parte de un GUID sea único ; de hecho, está lejos de ser único.
Lo más breve que sé que garantiza la singularidad global se presenta en esta publicación de blog de Jeff Atwood . En la publicación vinculada, analiza varias formas de acortar un GUID y, al final, lo reduce a 20 bytes a través de la codificación Ascii85 .
Sin embargo, si absolutamente necesita una solución que no tenga más de 15 bytes, me temo que no tiene otra opción que usar algo que no esté garantizado como único a nivel mundial.
Los valores de IDENTIDAD deben ser únicos en una base de datos, pero debe tener en cuenta las limitaciones ... por ejemplo, hace que las inserciones de datos masivos sean básicamente imposibles, lo que lo ralentizará si está trabajando con una gran cantidad de registros.
También puede utilizar un valor de fecha / hora. He visto varias bases de datos donde usan la fecha / hora para ser el PK, y aunque no es súper limpio, funciona. Si controla las inserciones, puede garantizar efectivamente que los valores serán únicos en el código.
Para mi aplicación local, estoy usando este enfoque basado en el tiempo:
/// <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;
}
aquí mi solución, no es segura para la concurrencia, no más de 1000 GUID por segundo y seguro para subprocesos.
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 no optimizado, solo muestra !.
UtcNow
devuelva un valor de tic único por cada milisegundo: según los comentarios , la resolución depende del temporizador del sistema. Además, ¡será mejor que se asegure de que el reloj del sistema no cambie hacia atrás! (Dado que la respuesta del usuario 13971889 colocó esta pregunta en la parte superior de mi feed y critiqué esa respuesta, creo que debería repetir esa crítica aquí).
Si su aplicación no tiene unos pocos MILLONES de personas, usando esa cadena de generación única y corta en el MISMO MILISEGUNDO, puede pensar en usar la siguiente función.
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());
}
cambie yyyy a yy si solo necesita usar su aplicación en los próximos 99 años.
Actualización 20160511 : Función aleatoria correcta
- Agregar objeto de bloqueo
- Mover variable aleatoria fuera de la función RandomString
Ref
lock
es permitirle reutilizar la misma Random
instancia. ¡Creo que olvidaste borrar esa línea!
Sé que está bastante lejos de la fecha de publicación ... :)
Tengo un generador que produce solo 9 caracteres Hexa , por ejemplo: 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");
}
}
Basado en la respuesta de @ dorcohen y el comentario de @ pootzko. Puedes usar esto. Es seguro sobre el cable.
var errorId = System.Web.HttpServerUtility.UrlTokenEncode(Guid.NewGuid().ToByteArray());
Jzhw2oVozkSNa2IkyK4ilA2
o pruebe usted mismo en dotnetfiddle.net/VIrZ8j
Basado en algunos otros, aquí está mi solución que proporciona un guid codificado diferente que es seguro para URL (y Docker) y no pierde ninguna información:
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("=", "").Replace("+", "-").Replace("/", "_");
Los resultados de ejemplo son:
BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig
En C #, un long
valor tiene 64 bits, que si se codifica con Base64, habrá 12 caracteres, incluido 1 relleno =
. Si recortamos el relleno =
, habrá 11 caracteres.
Una idea loca aquí es que podríamos usar una combinación de Unix Epoch y un contador para un valor de época para formar un long
valor. El Unix Epoch en C # DateTimeOffset.ToUnixEpochMilliseconds
está en long
formato, pero los primeros 2 bytes de los 8 bytes son siempre 0, porque de lo contrario el valor de fecha y hora será mayor que el valor máximo de fecha y hora. Entonces eso nos da 2 bytes para colocar un ushort
contador.
Entonces, en total, siempre que el número de generación de ID no exceda 65536 por milisegundo, podemos tener una ID única:
// 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()
No es ciencia espacial volver a convertir, así que les dejo eso.
Si no necesita escribir la cadena, puede usar lo siguiente:
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());
}
Esto convertirá el Guid en una cadena de 8 caracteres como esta:
{b77a49a5-182b-42fa-83a9-824ebd6ab58d} -> "䦥 띺 ᠫ 䋺 ꦃ 亂 檽 趵"
{c5f8f7f5-8a7c-4511-b667-8ad36b446617} -> " 엸 詼 䔑 架 펊 䑫 ᝦ"
Aquí está mi pequeño método para generar una identificación única aleatoria y corta. Utiliza un rng criptográfico para la generación segura de números aleatorios. Agregue los caracteres que necesite a la chars
cadena.
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 no perder caracteres (+ / -) y si quieres usar tu guid en una url, debes transformarlo en base32
por 10000000 sin llave 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;
}
Puedes probar con la siguiente 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
devuelva un valor de tic único por cada milisegundo: según los comentarios , la resolución depende del temporizador del sistema. Además, ¡será mejor que se asegure de que el reloj del sistema no cambie hacia atrás! (La respuesta de ur3an0 también tiene estos problemas.)
puedes usar
code = await UserManager.GenerateChangePhoneNumberTokenAsync(input.UserId, input.MobileNumber);
solo sus 6
personajes agradables 599527
,143354
y cuando el usuario lo virifica simplemente
var result = await UserManager.VerifyChangePhoneNumberTokenAsync(input.UserId, input.Token, input.MobileNumber);
espero que esto te ayude
Guid.NewGuid().ToString().Split('-').First()