How to guarantee the code mutual exclusive access in async method (no Monitor.Enter/Exit is allowed)

43 views Asked by At

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");
        }
    }
}
0

There are 0 answers