I have this function PopulateUsers
called from c# Controller, that throws an exception "Object synchronization method was called from an unsynchronized block of code." - I have just found the reason on internet why - but need advice how to refactor my method so it is thread safe..
I am not supposed to call await function within the Monitor.Enter
and Monitor.Exit
.
But as you see in my code I am calling await _userManager.CreateAsync(account)
and I have to await
it - there is no sync version of the create function on user manager. I can take that code out of Monitor protection block but then how do I guarantee that the code is not going to be accessed from two different threads (two different user http calls)? I am also accessing database within the Monitor.Enter/Exit
protection block.
How do I solve that? Any ideas?
private async Task PopulateUsers(string path)
{
log.Info("UploadAccounts before locking");
Monitor.Enter(lockObject);
using (IDbContextTransaction transaction = _context.Database.BeginTransaction())
{
try
{
... some code here
DateTime dateTime = DateTime.ParseExact(request.Dob, ConstantsDefined.DateTimeFormat, System.Globalization.CultureInfo.InvariantCulture);
Account account = _context.Accounts.SingleOrDefault(x => x.Email == request.Email && x.DOB == dateTime);
// Validate
if (account == null)
{
// Account does not exist yet - create one
// map model to new account object
account = _mapper.Map<Account>(request);
account.Created = DateTime.UtcNow;
account.Verified = DateTime.UtcNow;
account.ScheduleGroup = group.ToString();
// hash password
account.PasswordHash = BC.HashPassword(request.Password);
account.UserFunctions = new List<Entities.Function>();
account.Schedules = new List<Schedule>();
var result = await _userManager.CreateAsync(account);
Debug.Assert(result != null && IdentityResult.Success.Succeeded == result.Succeeded);
}
_context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine(Thread.CurrentThread.Name + "Error occurred.");
log.Error(Thread.CurrentThread.Name + "Error occurred in Create:", ex);
throw;
}
finally
{
Monitor.Exit(lockObject);
log.Info("Create after locking");
}
}
}