Reputation: 41
First, I can't modify the design of the table to turn on identity specification for the primary key.
My question is how to handle in EF where user A and user B will have no conflict in primary key when adding a new record.
I'm working on Asp.Net MVC5 project.
My current code:
public void NewFooMember(FooMember fooMember)
{
fooMember.NullGuard("fooMember");
// This is the primary key
fooMember.MemberKey = NextFooMemberKey();
Repo.Add(fooMember);
UnitOfWork.Commit();
}
public int NextFooMemberKey()
{
int? maxFooMemberKey = Repo.Data.Max(fm => (int?)fm.MemberKey);
if (!maxFooMemberKey.HasValue)
{
return 1;
}
return maxFooMemberKey.Value + 1;
}
Upvotes: 1
Views: 444
Reputation: 41490
Your current idea has the flaw that two different calls could be made to NextFooMemberKey
at the same time, before either inserts a new records, resulting in two records being inserted into the Db with the same key.
To address this, you should be sure to make this sequence thread-safe, and add lock
calls around the key generation and insert functionality to ensure that only one thread can run that section of code at a time (ensuring unique keys).
You could also do with some optimization on the key generation function - assuming that you aren't dealing with multiple servers, you can cache the last key and just increment it each time - that way you don't have to round-trip to the database each time that you are generating a key (you would need to address the scenario where an insert fails, in which case you would need to decrement your static saved key).
private object newFooLock = new object();
public void NewFooMember(FooMember fooMember)
{
fooMember.NullGuard("fooMember");
lock (newFooLock) { // now only one thread can get in here at a time
try
{
fooMember.MemberKey = NextFooMemberKey();
Repo.Add(fooMember);
UnitOfWork.Commit();
}
catch (Exception ex)
{
lastFooKey--; // decrement saved value
}
}
}
private object newFooKeyLock = new object();
private static int? lastFooKey = null;
public int NextFooMemberKey()
{
// also lock the key generation, just to be safe
lock (newFooKeyLock) {
if (lastFooKey == null)
{
// only get from the db if the local value is not yet populated
lastFooKey = Repo.Data.Max(fm => (int?)fm.MemberKey) ?? 1;
}
lastFooKey++;
return lastFooKey.Value;
}
}
Upvotes: 2