0

I'm facing an issue regarding how to initialize background tasks in a loop in asp.net MVC.

My context is related to competitions. A competition can start as soon as the competitors number is reached, or else after a delay. The user choses how many competitions he wants to join (vm.InscriptionsNumber), and then calls the following Compete method.

If there is no existing competition he can join, I have to create one and initialize this new competition's timer, TryStartCompetition. When called, this method checks if the competition has already started (because the competitors number was reached), and if not, calls the StartCompetition method.

[HttpPost]
public async Task<ActionResult> Compete(CompetitionsViewModel vm)
{
    for (int i = 0; i < vm.InscriptionsNumber; i++)
    {
        Competition competition = UnitOfWork.CompetitionRepo.Get(...);

        if (competition == null) // If there is no existing competition the user can join
        {
            // Create a new one
            competition = new Competition(...);
            await UnitOfWork.CompetitionRepo.AddOrUpdate(competition);

            // Initialize the start max delay
            HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
            {
                await Task.Delay(Competition.Delay);
                await TryStartCompetition(competition);
            });
         }
        else if (competitorsNumberConditionOk)
        {
            await StartCompetition(competition);
        }

        // Other stuff...
    }

    await UnitOfWork.Save();

    return PartialView("~/Views/Competitions/Index.cshtml", ViewModel);
}

private async Task TryStartCompetition(Competition compet)
{
    // Competition has not started yet
    if (!compet.StartDate.HasValue)
    {
        await StartCompetition(compet);
        await UnitOfWork.Save();
    }
}

private async Task StartCompetition(Competition compet)
{
    // Competition ranking calculation and other stuff here needing async calls
    compet.StartDate = DateTime.Now;
}

On the first await call in StartCompetition the following exception is raised. I don't know how I should handle it in this particular context.

A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.

If vm.InscriptionsNumber equals 1 everything is okay but as soon as there is actually a loop, the error appears.

If anyone can help me understand why this occurs, I would be really grateful.

Sean
  • 507
  • 1
  • 9
  • 27
Flash_Back
  • 565
  • 3
  • 8
  • 31
  • https://stackoverflow.com/questions/34773533/why-does-await-not-appear-to-prevent-second-operation-on-ef-context – d.moncada Apr 22 '16 at 15:06
  • Hi, the post you linked seems to be about lazy loading, however the `await` method which raises the exception is a simple `Add`, I am doing lazy loading somewhere ? – Flash_Back Apr 22 '16 at 15:11
  • you might.. but it seems like overall, it has to do w/ multiple EF calls. is the EF context getting Disposed after each iteration? – d.moncada Apr 22 '16 at 15:12
  • If that is what you mean I don't think that the context is disposed between the first iteration of the `for` loop, and the second. The `UnitOfWork` is initialized once in the controller constructor. Would I need to dispose it and recreate it at the end of the `for` loop then ? – Flash_Back Apr 22 '16 at 15:15
  • Yeah, try that.. i think that's the issue, multiple Tasks accessing the same context. – d.moncada Apr 22 '16 at 15:26
  • 1
    You're using `QueueBackgroundWorkItem` to call `UnitOfWork.Save` on a background thread, and you're also calling `UnitOfWork.Save` on the controller's request thread. EF contexts cannot be used simultaneously by multiple threads. – Stephen Cleary Apr 22 '16 at 16:01
  • That's actually true but I don't know how I could do differently since I have to save in both ways. I'm currently trying to follow `d.moncada` advice but I am not really sure where I should dispose the context or do a `using`, thus I have lots of issues regarding entities attached twice etc. – Flash_Back Apr 22 '16 at 16:10

0 Answers0