I inherited an app which has a service class which is called from asp.net application like this:
class PeopleService : IPeopleService
{
//...
public void SavePerson(Person person)
{
person.UniqueId = _idGenerationService.GetNextId(person);
//...
_dbContext.InsertOrUpdate(person);
_dbContext.Commit();
}
}
class IdGenerationService : IIdGenerationService
{
//...
public string GetNextId(Person person)
{
int nextId = _dbContext.People.Count(x => x.Gender == person.Gender) + 1;
return string.Format("AB{0}", nextId);
}
}
The implementation of GetNextId(Person)
is not thread safe, and we had a lot of Person objects with duplicated ids, like AB1, AB2, AB2, etc...
The solution was to apply lock
to SavePerson(Person)
like this:
class PeopleService : IPeopleService
{
private static readonly Object ReadIdLock = new Object();
//...
public void SavePerson(Person person)
{
lock(ReadIdLock)
{
person.UniqueId = _idGenerationService.GetNextId(person);
//...
_dbContext.InsertOrUpdate(person);
_dbContext.Commit();
}
}
}
Now we tried to test the code by hitting the Save button simultaneously in asp.net app. But the fix didn't work! Interestingly, only the first records seem to be duplicates, like AB1, AB1, AB2, AB3...
How it's possible that multiple requests have accessed the locked statements? Should I use Mutex
instead?
(Please note this example is not a production code, it's a simplified code to convey the problem that we are having. Also, small point, we are using StructureMap as DI).