views:

98

answers:

5

I am having difficulty with the following code which is inside a static method of a non-static class.

int iRand;
int rand;

rand = new Random((int)DateTime.Now.Ticks);
iRand = rand.Next(50000);

The iRand number, along with some other values, are being inserted into a new row of an Access MDB table via OLEDB. The iRand number is being inserted into a field that is part of the primary key, and the insert attempt is throwing the following exception even though the iRand number is supposed to be random:

System.Data.OleDb.OleDbException: The changes you requested to the table were not successful because they would create duplicate values in the index, primary key, or relationship.  Change the data in the field or fields that contain duplicate data, remove the index, or redefine the index to permit duplicate entries and try again.

Could the fact the method is static be making the iRand number stay the same, for some reason?

+2  A: 

No, being in a static method will make no difference. However, if you call your method repeatedly in a short space of time, you'll end up creating the same seed. That in turn means you will get the same values produced from it. By the way, the parameterless Random constructor already initializes itself from the current date/time, so your explicit use of it is redundant.

You should use a single instance of Random per thread (unfortunately Random isn't thread-safe).

You might want to read my blog post around this - as well as the comments. (At some point I'm going to rewrite it as another article, but this is what I've got at the moment.)

Jon Skeet
+4  A: 

You should not be using Random Numbers for primary keys! First of all, yes, there is a chance of collision, in your case, n/50000,where n is the number of previously created numbers. (Thanks, Guffa) Secondly, why not use sequential values? i.e. first begin with 1, 2, 3, etc. No collision, guaranteed unique.

On the other hand, I have come across the problem of random numbers being "stuck" in a cycle, and this occurred because Random is not thread-safe. This is how I handled it : http://stackoverflow.com/questions/2713760/c-random-number-generator-getting-stuck-in-a-cycle

Also, why not add some logging code so that you can examine the random numbers being output, and check how much data is inside the user's DB already? (Although I am pretty sure you did that already)

Jean Azzopardi
This wasn't my choice - I am debugging the code. But this is happening every time the user attempts this part of the code, so I'm not sure it is just 1/50000 in this case.
Craig Johnston
@Craig: Yes, it's not repeating random numbers that is your main problem, it's that you are seeding with the same tick value. The risk for a repeating random number really is n/50000 (where n is the number of previously created numbers), much lower than the risk of seeding with the the same tick value which is something closer to 99/100.
Guffa
I edited my post to reflect that, thanks.
Jean Azzopardi
A: 

Expanding on Jon's answer, your class should be structured like so:

static class MyClass
{

    static int iRand = new Random(); // default seed is based on ticks
    static int rand; 

    static void MyMethod()
    {
        rand= rand.Next(50000); 
    }
}
ck
Not quite - because you'd have to make `rand` static as well, at which point you've got a non-thread-safe static method, which isn't ideal...
Jon Skeet
+1  A: 

The reason is that you are creating Random objects that you seed with the same start value. When you create them too close in time, the DateTime.Now.Ticks value hasn't had time to change.

Create one Random object that you use for all the random numbers that you want to create in the class. You can send the object into the static method as a parameter.

Note that you will still get collisions sometimes, randomness doesn't guarantee uniqueness.

Guffa
+1  A: 

You shouldn't be using a random number as part of a composite primary key unless you are ABSOLUTELY sure that the other members of your key are enough to define the key - this error suggests that, in this case, they are not.

To be honest, I can't imagine why you would want a random in a PK, but hey - it's your show...

I always have my primary keys generated for me - even in Oracle which isn't quite as nice, I use Sequences to generate them.

And in terms of the Random number generation, see Guffa's answer.

Martin Milan
As mentioned, not my design. The problem I have is that the above exception appears to be occurring all the time, which is not consistent with a collision.
Craig Johnston
Profile the server to get the exact insert query being executed, and check that against the data in the table already...
Martin Milan