Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Creating a unique or semi-unique ID in .NET


:P
On this page:

I have a few applications where I need to create some unique IDs that can't be GUIDs. GUIDs are great if you truly need a globally unique identifier, but they are a bit of overkill when it comes to application that locally unique ids. GUIDS are nice but they are big. If you need to represent them as strings the 38 characters it uses with punctuation is a bit excessive. There's nothing that I hate more than browser querystrings that have 3 GUID IDs embedded in them <g>…

 

The most common places I need these simple ids are for temporary identifiers. For example when I send a request to my Credit Card Processor there's an ID that correlates my invoice with the transaction at the provider. The providers has some odd rules about the invoice numbers so if somebody needs to resubmit the order it won't go through – hence I need to create a new ID. Anyway, a GUID isn't what I'd want here.

 

Invoices too. I tend to give invoices an ID that is not just a number but yet reasonably easy for a user to work with. Handing a 34 digit numeric string isn't going to fly with customers.

 

So… in light of this, what's a good way to create an ID in .NET? In Fox I used to use SYS(2015) for this, which works pretty reliably except in very volume multi-threaded environments. Unfortunately I'm not sure what it uses to generate this id.

 

Invoice.Invno = DateTime.Now.ToString().GetHashCode().ToString("x");

 

which produces something like this:

 

369bf2e0

 

Now, I've never had a problem with this but reading over the documentation of GetHashCode() it indicates that GetHashCode() doesn't generate unique hashcodes. If the value isn't guaranteed to be unique how can it be used as a hash of a value? That's a bit confusing…

 

In regards to strings then it seems to contradict the above:

 

For example, the implementation of GetHashCode provided by the String class returns unique hash codes for unique string values.

 

So, does that mean that any one string representation can be expressed only by a unique hash? I can't imagine this being true.

 

So if I do:

 

Guid.NewGuid().ToString().GetHashCode().ToString("x");

 

469cf3e1

 

Somehow I don't think that this string representation at least is unique… 38 characters represented as 8? Ok 32 bits, but still it's 8 digits and characters limited to hex values.

 

Here's an interesting link that talks about some of the GetHashCode inconsistencies with the documentation:

 

http://www.interact-sw.co.uk/iangblog/2004/06/21/gethashcode

 

Even with that in mind it's not really clear to me if generating a string with the values above would be safe or not.

 

So what other options are there to generate a reasonably unique ID?  Maybe a better way is:

 

DateTime.Now.Ticks.ToString("x")

 

which generates:

 

8c8113da982d75a

 

and which is getting a bit long at 15 characters. Ticks is a 64 bit so it makes sense that the string is a bit longer.

 

Hmm... for the invoice example I mentioned earlier I suppose using a hex representation of the PK would work. Or even a hashcode of the PK. Nope - Integer values persisted aren't properly random (10 turns into a and GetHashCode() for integers just returns the value).

 

What else? How about using part of a GUID string? Is there any part of a GUID that can be dissected and be acceptable as a non-network safe ID?  Doesn't look like it – if you generate new GUIDs every single character changes in the string representation.

 

 

 


The Voices of Reason


 

Hagay
March 09, 2006

# re: Creating a unique or semi-unique ID in .NET

GetHashCode job is to reutrn as unique as possible identifier.

in order to improve efficiency of hashtable structure the keys should be unique in best case.

.net's hashtable can work when you assign duplicate hashcodes, but its less efficent
(for each same hashcodes it builds an array/linked list and then perform Equals method on the values of that array to determine the correct one to return)

so in worst case, hashtable preformance reduced to linear search performance with quite a memory overhead :)

Martin Möbius
March 09, 2006

# re: Creating a unique or semi-unique ID in .NET

For the hashCode thing an older Java article. I expect that the most is true for .net too.

http://www-128.ibm.com/developerworks/java/library/j-jtp05273.html

For the UID problem. Why not an easy counter?
Just make it threadsafe with synchronized, or use the InterLock class.
Would be at least unique for the lifetime of the application.
You can persist the counter value in a database or a file before the application ends.
Or init the counter with DateTime.Now and the hash of the IP during application start. Would be at least unique for the machine. A restart of a crashed application hopfully takes longer than the tick for DateTime.Now.

Put the stuff into a singleton class.

Frank Camp
March 09, 2006

# re: Creating a unique or semi-unique ID in .NET

Hi Rick,

I've uploaded the routines to the UT a long time ago, to convert sys(2015) values to date/time and back.

The url is: http://www.universalthread.com/wconnect/wc.dll?2,54,33,9704

Craig Boyd
March 09, 2006

# re: Creating a unique or semi-unique ID in .NET

The message digest (hash) of a GUID is not guaranteed to be unique - all hash functions have theoretical collisions given that the message digest is a compressed representation of the original. However the chance of those collisions needs to be taken into consideration. With 8 hex digits you're looking at what? 16^8 permutations? That's 4,294,967,296 unique message digests. So, given a unique GUID string, would this ID be guaranteed to be unique? No, but pretty good for what you're proposing to use them for (this of course assumes that there aren't any major flaws with the hash algorithm or implementation that MS is using to derive the message digest). For the ID to be guaranteed unique you'd need to generate the ID and then check it against the current existing IDs to make sure it wasn't a duplicate. Or, you could increase your chances by hashing the GUID string using a higher level hash function such as SHA1 (160 bit). But then you're increasing the length of the ID again, so that's not really a solution. I think you've hit upon about the best solution there is with GetHashCode() of a GUID given what you intend to use it for.

Jeff Atwood
March 09, 2006

# re: Creating a unique or semi-unique ID in .NET

It is possible to compress GUIDs (128-bit numbers) down into more compact ASCII representations:

http://www.codinghorror.com/blog/archives/000409.html

That's 128 bits of data in 20 printable ASCII bytes. Not too shabby!

If you use smaller numbers, you have the pigeonhole principle to contend with. But the odds of it being an actual problem are so small.

Bertrand Le Roy
March 09, 2006

# re: Creating a unique or semi-unique ID in .NET

Yup, the mistake here is to use hexadecimal.

Steve
March 09, 2006

# re: Creating a unique or semi-unique ID in .NET

Craig, very good response.

Rick Strahl
March 09, 2006

# re: Creating a unique or semi-unique ID in .NET

Jeff - it figures you'd been down this path before <g>... Great link!

Hex encoding the GUID is not that different from just stripping out the dashes. Interesting to compare these two values:

Guid g = Guid.NewGuid();
string strGuid1 = g.ToString();

StringBuilder sb = new StringBuilder();

// *** Hex definition
byte[] byt = g.ToByteArray();
foreach (Byte b in byt)
{
sb.AppendFormat("{0:x2}", b);
}
string strGuid2 = sb.ToString();

Bertrand - one advantage of using Hex is reasonably easy to read even with a mix of numbers and digits.

So given all this talk about Guids - how about the DateTime string or the TickCount? That string will be fairly short compared to Guid and unique and should provide a more reliable hash value assuming you can deal with the possible time conflict (down to the millisecond).

Havagan
March 10, 2006

# re: Creating a unique or semi-unique ID in .NET

I know zero about crypto (so ignore me if this makes no sense) but wouldn't a function similiar to one which generates random passwords using the System.Security.Cryptography.RNGCryptoServiceProvider class create a reasonably random value if you limit the allowed characters to only alpha-numeric?

Public Function GetRandomPassword() As String
Dim maxSize as Integer = 20
Dim minSize as Integer = 15
Dim chars() as Char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray
Dim size As Integer = maxSize
Dim data(1) as Byte
Dim crypto As new RNGCryptoServiceProvider
crypto.GetNonZeroBytes(data)
size = data(data(0) Mod (maxSize - minSize)) + minSize
ReDim data(size)
crypto.GetNonZeroBytes(data)
Dim result As New StringBuilder(size)
For Each b As Byte in data
result.Append(chars(b Mod (chars.Length - 1)))
Next
Return result.ToString
End Function



joe
March 10, 2006

# re: Creating a unique or semi-unique ID in .NET

Rick - be careful using GetHashCode.
The implementation changed from .Net 1.1 to 2.0. So the same string value returns different values depending on the framework. This is why there is a warning to not store the hascode value in a database and expect to be able to re-generate it across .Net versions.

Anon
March 13, 2006

# re: Creating a unique or semi-unique ID in .NET

How about using a simple Base36 structure? http://www.codeproject.com/csharp/base36.asp

# DotNetSlackers: Creating a unique or semi-unique ID in .NET


Darrell
February 22, 2007

# re: Creating a unique or semi-unique ID in .NET

Rick,

Did you ever decide on a solution to this issue?

I wonder if System.IO.Path.GetRandomFileName() is comparable to sys(2015).

# A Continuous Learner's Weblog: March 2006


Lautz of .NET
June 11, 2007

# Lautz of .NET - Not so Random, Pseudo-Daily Links of Interest #1


Jorge Ojeda Belmar
December 27, 2007

# re: Creating a unique or semi-unique ID in .NET

IMHO I think that it's easer to use an identity value from DataBase.
What do you think?

Oscar
March 24, 2008

# re: Creating a unique or semi-unique ID in .NET

Hi. Has anyone tried the Base36 method above? Results?

ALexander Nel
August 06, 2008

# re: Creating a unique or semi-unique ID in .NET

Hi all, try this:

Dim oGUID As New Guid()
Dim g As Guid = Nothing
g = Guid.NewGuid()
Dim sGUID As String = g.ToString()
sGUID = sGUID.Replace("-", "")
sGUID = sGUID.Substring(7, 25)

Tested it on 1000001 GUID's, with no doubles found. In this case I only had 25 characters to use as a Unique Identifier.

Remember to import: System.GUID

Cheers

Max Pool
March 17, 2011

# re: Creating a unique or semi-unique ID in .NET

I am facing this exact issue only faced with only 13 character limit. Although, still completely possible to collide, I used this pattern to minimize the collisions even more:

DateTime.Now.Year.ToString() + Guid.NewGuid().ToString().GetHashCode().ToString("x");

Perfect? No....but at least my probability of 1:16^8 colliding starts over every year....

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024