tags:

views:

248

answers:

5

In the application, when special types of objects are created, I need to generate a unique-id for each of them. The objects are created thro' a factory and have a high possibility of being created in a 'bulk' operation. I realize that the "Random" from the framework is not so 'random' after all, so I tried appending the time-stamp as follows:

private string GenerateUniqueId()
{
    Random randomValue = new Random();
    return DateTime.Now.Ticks.ToString() + randomValue.Next().ToString();
}

Unfortunately, even this does not work. For objects that are created in rapid succession, I generate the same Unique Id :-(

Currently, I am implementing it in a crude way as follows:

private string GenerateUniqueId()
{
    Random randomValue = new Random();
    int value = randomValue.Next();
    Debug.WriteLine(value.ToString());
    Thread.Sleep(100);
    return DateTime.Now.Ticks.ToString() + value.ToString();
}

Since this is not a very large application, I think a simple and quick technique would suffice instead of implementing an elaborate algorithm.

Please suggest.

+10  A: 

A GUID is probably what you're looking for:

private string GenerateUniqueId()
{
    return Guid.NewGuid().ToString("N");
}

If you want a smaller, more manageable ID then you could use something like this:

private string GenerateUniqueId()
{
    using (var rng = new RNGCryptoServiceProvider())
    {
        // change the size of the array depending on your requirements
        var rndBytes = new byte[8];
        rng.GetBytes(rndBytes);
        return BitConverter.ToString(rndBytes).Replace("-", "");
    }
}

Note: This will only give you a 64-bit number in comparison to the GUID's 128 bits, so there'll be more chance of a collision. Probably not an issue in the real world though. If it is an issue then you could increase the size of the byte array to generate a larger id.

LukeH
@Codex: The Thread.Sleep is a horrible idea. You are going to kill your apps performance like this.
casperOne
@Luke: Thanks. But further I use the UniqueId as filename to serialize the object to disk. Its a bit cumbersome to handle@casperOne: Point noted! Will get rid of it soon...
Codex
@Codex: You can reformat the GUID if you want to get rid of some of the delimiters for your file name, but the GUID class will ensure you have a unique ID, which is what you need.
Jay S
@Luke: Thanks! Both of the approaches work well. I chose the first one.
Codex
+1  A: 

Assuming you do not want a GUID, First option would be a static field, and interlocked:

private static long lastId = 0
private static long GetNextId() {
  return Interlocked.Increment(ref lastId);
}

If you want something based on time ticks, remember the last value and if the same manually increment and save; otherwise just save:

private static long lastTick = 0;
private static object idGenLock = new Object();
private static long GetNextId() {
  lock (idGenLock) {
    long tick = DateTime.UtcNow.Ticks;
    if (lastTick == tick) {
      tick = lastTick+1;
    }
    lastTick = tick;
    return tick;
  }
}

(Neither of these approaches will be good with multiple processes.)

Richard
+1  A: 

In your comments Codex you say use the unique ID as a file name. There is a specific function for generating cryptographically secure file names, Path.GetRandomFileName()

As it's cryptographically secure these would be unique even in batch operations. The format is a little horrible though as they're optimised for filenames, but it may work for other references as well.

blowdart
@blowdart:This looks promising. Thanks!
Codex
A: 

Why can't your factory (which is presumably single-threaded) generate sequential unique integers? If you expected Random() to work, why not Guid() (or whatever is equivalent)?

le dorfier
A: 

If you're going to resort to coding your own UUID-generator, make sure you salt the generator.

I suggest you check out the open source package ossp-uuid, which is an ISO-C API and CLI for generating Universally Unique Identifiers.

Nerdling